常见四种限流算法详解(附:javaDemo)

news2024/10/22 23:34:44

限流简介

现代互联网很多业务场景,比如秒杀、下单、查询商品详情,最大特点就是高并发,而往往我们的系统不能承受这么大的流量,继而产生了很多的应对措施:CDN、消息队列、多级缓存、异地多活。

但是无论如何优化,终究由硬件的物理特性决定了我们系统性能的上限,如果强行接收所有请求,往往造成雪崩。

这时候限流熔断就发挥作用了,限制请求数,快速失败,保证系统满负载又不超限。

极致的优化,就是将硬件使用率提高到100%,但永远不会超过100%

计数器法

计数器法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说,我们1秒钟的访问次数 不能超过100次。那么我们可以这么做:在一开始的时候,我们可以设置一个计数器counter,每当一个请求过来的 时候,counter就加1,如果counter的值大于100并且该请求与第一个请求的间隔时间还在1秒钟之内,那么说明请 求数过多;如果该请求与第一个请求的间隔时间大于1秒钟,且counter的值还在限流范围内,那么就重置 counter。

 

 具体算法的伪代码:

/**
 * 计数器限流算法
 */
public class CounterLimiter {
    private long timeStamp = System.currentTimeMillis();
    private int reqCount; // 请求次数
    private int limitNum = 10; // 每秒限流的最大请求数
    private long interval = 3000L; // 时间窗口时长,单位ms

    /**
     * @return 返回true代表限流,false代表通过
     */
    public synchronized Boolean limit() {
        long now = System.currentTimeMillis();
        if (now < timeStamp + interval) { // 在当前时间窗口内
        // 判断当前时间窗口请求数加1是否超过每秒限流的最大请求数
            if (reqCount + 1 > limitNum) {
                return true;
            }
            reqCount++;
            return false;
        } else { //开启新的时间窗口
            timeStamp = now;
        // 重置计数器
            reqCount = 1;
            return false;
        }
    }
}

滑动时间窗口算法

滑动时间窗口,又称rolling window。为了解决计数器法统计精度太低的问题,引入了滑动窗口算法。下面这张 图,很好地解释了滑动窗口算法:

在上图中,整个红色的矩形框表示一个时间窗口,在我们的例子中,一个时间窗口就是一秒钟。然后我们将时间窗 口进行划分,比如图中,我们就将滑动窗口划成了10格,所以每格代表的是100毫秒。每过100毫秒,我们的时间 窗口就会往右滑动一格。我们会根据请求发生的时间找到对应的时间窗格,然后在窗格里维护一个计数器 counter,并让其加1,比如当一个请求在550毫秒的时候到达,那么500-600毫秒对应窗格里的counter就会加1。 计数器算法其实就是滑动窗口算法。只是它没有对时间窗口做进一步地划分,所以只有1个窗格。 由此可见,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

具体算法的伪代码:

/**
 * 滑动时间窗口限流实现
 * 假设某个服务最多只能每秒钟处理100个请求,我们可以设置一个1秒钟的滑动时间窗口,
 * 窗口中有10个格子,每个格子100毫秒,每100毫秒移动一次格子,每个格子记录当前100毫秒内的请求次数
 */
public class SlidingTimeWindowLimiter {

    // 统计周期 1000ms
    public static final int TIME_WINDOW = 1000;

    private static final LinkedList<Long> list = new LinkedList<>();

    // 时间窗口内允许的最大请求数
    private final int maxCount;

    public SlidingTimeWindowLimiter(int maxCount) {
        this.maxCount = maxCount;
    }

    public synchronized boolean limit() {
        // 获取当前时间
        long nowTime = System.currentTimeMillis();
        // 如果队列还没满,则允许通过,并添加当前时间戳到队列开始位置
        if (list.size() < maxCount) {
            list.addFirst(nowTime);
            return true;
        }

        // 队列已满(达到限制次数),则获取队列中最早添加的时间戳
        Long farTime = list.getLast();
        // 用当前时间戳 减去 最早添加的时间戳
        if (nowTime - farTime <= TIME_WINDOW) {
            // 若结果小于等于timeWindow,则说明在timeWindow内,通过的次数大于count
            // 不允许通过
            return false;
        } else {
            // 若结果大于timeWindow,则说明在timeWindow内,通过的次数小于等于count
            // 允许通过,并删除最早添加的时间戳,将当前时间添加到队列开始位置(开始滑动)
            list.removeLast();
            list.addFirst(nowTime);
            return true;
        }
    }
}

漏桶算法

漏桶算法,又称leaky bucket。

从图中我们可以看到,整个算法其实十分简单。首先,我们有一个固定容量的桶,有水流进来,也有水流出去。对 于流进来的水来说,我们无法预计一共有多少水会流进来,也无法预计水流的速度。但是对于流出去的水来说,这 个桶可以固定水流出的速率。而且,当桶满了之后,多余的水将会溢出。 我们将算法中的水换成实际应用中的请求,我们可以看到漏桶算法天生就限制了请求的速度。当使用了漏桶算法, 我们可以保证接口会以一个常速速率来处理请求。所以漏桶算法天生不会出现临界问题。

具体的伪代码如下: 

/**
 * 漏桶限流算法
 */
public class LeakyBucketLimiter {
    /**
     * 桶的容量
     */
    private final long capacity = 20L;

    /**
     * 水流出的数量速度
     */
    private final long rate = 10L;
    /**
     * 水流出的时间速度
     */
    private final long rateTime = 1000L;

    /**
     * 当前水量(实际上就是请求数)
     */
    private long water = 0L;

    /**
     * 上次漏水时间
     */
    private long lastTime = System.currentTimeMillis();

    public synchronized boolean limit() {
        long now = System.currentTimeMillis();
        //计算当前水量
        long distance = now - lastTime;
        water = Math.max(0, water - (distance / rateTime) * rate);
        lastTime = now;
        //判断剩余空间是否足够
        if ((water + 1) < capacity) {
            water++;
            return true;
        } else {
            return false;
        }
    }

}

令牌桶算法

令牌桶算法,又称token bucket。同样为了理解该算法,我们来看一下该算法的示意图:

从图中我们可以看到,令牌桶算法比漏桶算法稍显复杂。首先,我们有一个固定容量的桶,桶里存放着令牌 (token)。桶一开始是空的,token以 一个固定的速率r往桶里填充,直到达到桶的容量,多余的令牌将会被丢 弃。每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。

具体的伪代码如下: 

/**
 * 令牌桶限流算法
 */
public class TokenBucketLimiter{
    private long lastTime = System.currentTimeMillis();
    /**
     * 桶的容量
     */
    private long capacity = 40;
    /**
     * 当前令牌数量
     */
    private long tokens = 0;
    /**
     * 令牌放入数量速度
     */
    private long rate = 10;
    /**
     * 令牌放入时间速度
     */
    private final long rateTime = 1000L;
    
    /**
     * @return 返回true代表限流,false代表通过
     */
    public synchronized Boolean limit() {
        long now = System.currentTimeMillis();
        // 先添加令牌
        // 计算当前令牌数,令牌数最大为桶的容量
        long distance = now - lastTime;
        tokens = Math.min(capacity, tokens + distance * rate/ rateTime);

        lastTime = now;
        if (tokens < 1) {
            // 若不到1个令牌,则拒绝
            return true;
        } else {
            // 还有令牌,领取令牌
            tokens--;
            return false;
        }
    }
}

限流算法小结

计数器 VS 滑动窗口

1 计数器算法是最简单的算法,可以看成是滑动窗口的低精度实现。

2 滑动窗口由于需要存储多份的计数器(每一个格子存一份),所以滑动窗口在实现上需要更多的存储空间。

3 也就是说,如果滑动窗口的精度越高,需要的存储空间就越大。

漏桶算法 VS 令牌桶算法

1 漏桶算法和令牌桶算法最明显的区别是令牌桶算法允许流量一定程度的突发。

2 因为默认的令牌桶算法,取走token是不需要耗费时间的,也就是说,假设桶内有100个token时,那么可以瞬间允许100个请求通过。

3 当然我们需要具体情况具体分析,只有最合适的算法,没有最优的算法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1498513.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Material Studio 中 DMol3 计算材料吸附能

1.先导入Cif文件 2.切表面 3.沿着你要切的晶面切 4.扩胞 5.加真空层&#xff08;一般加10埃&#xff09; 现在就是这样的了 6.然后对其结构优化&#xff08;高斯几何优化&#xff09; 7.再在体系上加原子或者想要的材料 8.Outmal文件中最后的Ef就是整个体系的能量&#xff0…

腾讯云哪款服务器最便宜划算?2024腾讯云服务器优惠价格表

腾讯云优惠活动2024新春采购节活动上线&#xff0c;云服务器价格已经出来了&#xff0c;云服务器61元一年起&#xff0c;配置和价格基本上和上个月没什么变化&#xff0c;但是新增了8888元代金券和会员续费优惠&#xff0c;腾讯云百科txybk.com整理腾讯云最新优惠活动云服务器配…

2024第二届语言,教育与艺术鉴赏国际会议(ICLEAA 2024)

2024第二届语言&#xff0c;教育与艺术鉴赏国际会议&#xff08;ICLEAA 2024&#xff09; 一、【会议简介】 我们非常荣幸地邀请您参加2024第二届语言&#xff0c;教育与艺术鉴赏国际会议&#xff08;ICLEAA 2024&#xff09;&#xff0c;该会议将在美丽的苏州举行。 ICLEAA …

Mybatis-Plus Mapper映射文件使用

介绍 MyBatis 的真正强大在于它的语句映射&#xff0c;这是它的魔力所在。由于它的异常强大&#xff0c;映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比&#xff0c;你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本&#xff0…

一篇文章带你通关并查集(持续更新中)

这篇文章的所有题目均来自于自行整理&#xff0c;代码均来自于自行梳理调试&#xff08;思路可能比较暴力&#xff09;。初衷在于整理练习思路&#xff0c;且起到督促自己学习的作用 本文分成将三个模块 1.普及组 &#xff08;洛谷黄题&#xff09; 2.提高组 &#xff08;洛…

OracleXE112、plsqldev1207的安装和基本配置

OracleXE112、plsqldev1207的安装和基本配置 OracleXE112、plsqldev1207的安装和基本配置Oracle安装oracle是什么Oracle两个版本下载安装包 安装OracleXE112_Win64注意&#xff1a;安装到空目录下&#xff1b;输入口令&#xff08;记住啊&#xff01;&#xff09;安装成功&…

基于springboot+vue的在线远程考试系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

别再盲目推广!用Xinstall精准追踪App下载渠道

在移动互联网时代&#xff0c;App推广已经成为企业获取用户、提升品牌知名度的重要手段。然而&#xff0c;面对众多的推广渠道和复杂的用户行为&#xff0c;如何精准统计App下载渠道来源&#xff0c;成为了广告主和开发者亟待解决的问题。今天&#xff0c;我们就来聊聊国内专业…

我的创作周年纪念日

机缘 最初成为创作者的初心&#xff1a;整理自己的知识体系&#xff0c;普及前端知识 实战项目中的经验分享日常工作学习过程中的记录通过文章进行技术交流归纳和整理自己的知识体系 收获 创作的过程中收获&#xff1a; 获得了909粉丝的关注获得了很多正向的反馈&#xff0c…

Leetcoder Day42| 动态规划part09 打家劫舍问题

198.打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房…

Sleuth(Micrometer)+ZipKin分布式链路追踪

Sleuth(Micrometer)ZipKin分布式链路追踪 Micrometer springboot3之前还可以用sleuth&#xff0c;springboot3之后就被Micrometer所替代 官网https://github.com/spring-cloud/spring-cloud-sleuth 为什么会出现这个技术&#xff1f; 在微服务框架中&#xff0c;一个由客户…

JAVA语言基础 JAVA入门

注释 单行注释&#xff1a;用双斜线 // 表示 多行注释&#xff1a;用 /*------------------*/ 表示 文档注释&#xff1a;用 /**-----------------*/ 表示 分隔符 常见的分隔符有&#xff1a;分号 ; 花括号 {} 方括号 [ ] 圆括号 () 空格 圆点 . 在 Java 语言中每一条…

Linux:文件权限详解及修改方法

文章目录 1、Linux文件权限1.1、如何查看到文件权限1.2、ll命令介绍 2、权限分类2.1、文件权限2.2、文件夹权限 3、权限修改3.1、修改文件/文件夹权限1&#xff09;chmod指令2&#xff09;chmod指令符号 3.2、修改文件/文件夹所属用户3.3、修改文件/文件夹所属群组 4、参考 1、…

CXYGZL实现钉钉、飞书和微信全面覆盖!!!

非常欣慰能在这里与大家分享&#xff0c;CXYGZL已圆满实现多端互通的目标&#xff01;&#xff01;&#xff01; 无论您是在手机、电脑还是平板上使用钉钉、企微还是飞书&#xff0c;只需将CXYGZL轻松集成到您的办公软件中&#xff0c;即可实现无缝审批处理各项任务&#xff0c…

【你也能从零基础学会网站开发】Web建站之HTML+CSS入门篇 常用HTML标签(1)

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享 &#x1f40b; 希望大家多多支持, 我们一起学习和进步&#xff01; &#x1f3c5; 欢迎评论 ❤️点赞&#x1f4ac;评论 &#x1f4c2;收藏 &#x1f4c2;加关注 HTML中的双…

数据结构 - 堆

这篇博客将介绍堆的概念以及堆的实现。 1. 堆的定义&#xff1a; 首先堆的元素按照是完全二叉树的顺序存储的。 且堆中的某个节点总是不大于或不小于其父节点的值。 根节点最大的堆叫做大堆&#xff0c;根节点最小的堆叫小堆。逻辑结构如下图所示&#xff1a; 大堆和小堆的…

Spring揭秘:ApplicationContextAware应用场景及实现原理!

内容概要 ApplicationContextAware接口能够轻松感知并在Spring中获取应用上下文&#xff0c;进而访问容器中的其他Bean和资源&#xff0c;这增强了组件间的解耦&#xff0c;了代码的灵活性和可扩展性&#xff0c;是Spring框架中实现高级功能的关键接口之一。 核心概念 它能用…

腾讯云学生服务器详细介绍_学生服务器价格_学生机申请流程

2024年腾讯云学生服务器优惠活动「云校园」&#xff0c;学生服务器优惠价格&#xff1a;轻量应用服务器2核2G学生价30元3个月、58元6个月、112元一年&#xff0c;轻量应用服务器4核8G配置191.1元3个月、352.8元6个月、646.8元一年&#xff0c;CVM云服务器2核4G配置842.4元一年&…

Java开发从入门到精通(一):Java的基础语法高阶

Java大数据开发和安全开发 &#xff08;一)Java的流程控制1.1 分支语句1.1.1 IF分支语句第一种IF语句第二种IF-ELSE语句第三种IF-ELSE IF-ELSE语句if语句使用的几个常见问题 1.1.2 switch分支语句switch分支的执行流程switch分支的导学案例:电子备忘录if、switch的比较&#xf…

关于JavaScript你该知道的(错误)和不知道的(技巧)

关于JS你该知道的(错误)和不知道的(技巧) 你该知道的(容易犯的错误) 忽略异步函数// Incorrect: Ignoring the asynchronous nature of setTimeout console.log("Start"); setTimeout(() => console.log("Timeout"), 0); console.log("End&q…