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

限流简介

现代互联网很多业务场景,比如秒杀、下单、查询商品详情,最大特点就是高并发,而往往我们的系统不能承受这么大的流量,继而产生了很多的应对措施: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 {// 统计周期 1000mspublic 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.hqwc.cn/news/518366.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Selenium自动化测试-3.元素定位(1)

这次我们要分享的是对元素的定位&#xff0c;在一个页面中有很多不同的策略来定位一个元素&#xff0c;我们选择最合适的方法即可。 一个页面最基本组成单元是元素&#xff0c;想要定位一个元素&#xff0c;我们需要特定的信息来说明这个元素的唯一特征。 selenium 主要提供了…

2024 年 AI 辅助研发趋势

随着人工智能技术的持续发展与突破&#xff0c;2024年AI辅助研发正成为科技界和工业界瞩目的焦点。从医药研发到汽车设计&#xff0c;从软件开发到材料科学&#xff0c;AI正逐渐渗透到研发的各个环节&#xff0c;变革着传统的研发模式。在这一背景下&#xff0c;AI辅助研发不仅…

浅析assert宏

浅析assert宏 文章目录 浅析assert宏前言1. 简单的断言案例2. 断言与正常错误处理3. 断言的应用场景总结 前言 首先声明一点&#xff0c;断言&#xff08;assert&#xff09;是宏&#xff0c;而非函数。 ​ assert 宏的原型定义在 <assert.h>&#xff08;C&#xff09;、…

DIN11 OC系列导轨式光电隔离变送器模拟信号转换0-75mV0-50mV0-100mV转4-20mA0-5V0-10V

概述&#xff1a; 导轨安装DIN11 IPO OC系列模拟信号隔离放大器是一种将输入信号隔离放大、转换成按比例输出的直流信号混合集成厚模电路。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等需要直流信号隔离测控的行业。此系列产品内部采用了线性光电隔离技术相…

C++ 哈希表OJ

目录 1、1. 两数之和 2、面试题 01.02. 判定是否互为字符重排 3、217. 存在重复元素 4、 219. 存在重复元素 II 5、49. 字母异位词分组 频繁查找某一个数的时候可以使用哈希表&#xff0c;哈希表可以使用容器&#xff0c;也可以使用数组模拟&#xff0c;当元素是字符串中的字…

类和对象周边知识

再谈构造函数 前几期我们把六个默认成员函数一一说明后&#xff0c;构造函数还有一些周边知识。 初始化列表 我们在没有了解初始化列表的时候一般都是使用构造函数初始化或者在声明哪里给予缺省值&#xff0c;那么为什么好药存在初始化列表呢&#xff1f;是因为①.有些值必须…

软件测试面试200问(全)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、你的测试职业发展是什么&#xff1f; 测试经验越多&#xf…

CleanMyMac X4.14.7永久免费Mac电脑清理和优化软件

CleanMyMac X 是一款功能强大的 Mac 清理和优化软件&#xff0c;适合以下几类人群使用&#xff1a; 需要定期清理和优化 Mac 的用户&#xff1a;随着时间的推移&#xff0c;Mac 设备上可能会积累大量的无用文件、缓存和垃圾&#xff0c;导致系统运行缓慢。CleanMyMac X 的智能扫…

onlyOffice-windows 安装说明(二)

onlyoffice windows 安装 onlyoffice 支持多个平台比如&#xff1a;Windows Server、Linux、Docker 以下内容是对官网安装说明做了简单翻译&#xff0c;仅供参考&#xff0c;原文链接地址参见文末。 社区版允许您在本地服务器上安装ONLYOFFICE文档&#xff0c;并将在线编辑器…

stl的基本知识学习

1.vector&#xff1a; 2.set&#xff1a; 3.map&#xff1a; 4.栈&#xff1a; 5.队列&#xff1a; 6. unordered_map与unordered_set: 7. 位运算&#xff1a; 8.cctype&#xff1a; 导图&#xff1a;

C++ 路径问题

目录 例1 例2 例3 例4 例5 例6 例1 62. 不同路径 1.初始化 2.当前位置的条数&#xff0c;就是上面位置的条数 &#xff0c;加上其左边位置的条数&#xff0c;dp[i][j] dp[i - 1][j] dp[i][j - 1]; 参考代码 class Solution { public:int uniquePaths(int m, int n) …

CentOS上安装MySQL 5.7和MySQL 8.0教程

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…