4种经典的限流算法
0、基础知识
1000毫秒内,允许2个请求,其他请求全部拒绝。
不拒绝就可能往db打请求,把db干爆~
interval = 1000
rate = 2;
一、固定窗口限流
固定窗口限流算法(Fixed Window Rate Limiting Algorithm)是一种最简单的限流算法,其原理是在固定时间窗口(单位时间)内限制请求的数量。通俗点说主要通过一个支持原子操作的计数器来累计 1 秒内的请求次数,当 1 秒内计数达到限流阈值时触发拒绝策略。每过 1 秒,计数器重置为 0 开始重新计数。
/*** 固定窗口限流算法* 比如1000ms 允许通过10个请求* @author jeb_lin* 14:14 2023/11/19*/
public class FixWindowRateLimiter {private long interval; // 窗口的时间间隔private long rate; // 限制的调用次数private long lastTimeStamp; // 上次请求来的时间戳private AtomicLong counter; // 计数器public FixWindowRateLimiter(long interval,long rate){this.interval = interval;this.rate = rate;this.lastTimeStamp = System.currentTimeMillis();this.counter = new AtomicLong(0);}public static void main(String[] args) {// 比如1000ms 允许通过10个请求FixWindowRateLimiter limiter = new FixWindowRateLimiter(1000,2);for (int i = 0; i < 4; i++) {if(limiter.allow()){System.out.println(i + " -> ok ");} else {System.out.println(i + " -> no");}}}private boolean allow() {long now = System.currentTimeMillis();if(now - lastTimeStamp > interval){counter.set(0L);lastTimeStamp = now;}if(counter.get() >= rate){return false;} else {counter.incrementAndGet();return true;}}
}
输出:
0 -> ok
1 -> ok
2 -> no
3 -> no
优点
- 简单易懂
缺陷
- 存在临界问题
本来就允许1秒内进来2个请求,这时候进来了4个
/*** 测试不正常的情况*/private static void testUnNormal() throws Exception{// 比如1000ms 允许通过10个请求FixWindowRateLimiter limiter = new FixWindowRateLimiter(1000,2);Thread.sleep(500);for (int i = 0; i < 4; i++) {if(limiter.allow()){System.out.println(i + " -> ok ");} else {System.out.println(i + " -> no");}Thread.sleep(250);}}
输出:
0 -> ok
1 -> ok
2 -> ok
3 -> ok
二、滑动窗口限流
滑动窗口限流算法是一种常用的限流算法,它将单位时间周期分为n个小周期,分别记录每个小周期内接口的访问次数,并且根据时间滑动删除过期的小周期。比如上图的示例中,每 500ms 滑动一次窗口就可以避免这种临界问题,可以发现窗口滑动的间隔越短,时间窗口的临界突变问题发生的概率也就越小,不过只要有时间窗口的存在,还是有可能发生时间窗口的临界突变问题。
类似于微积分:假如我切成1000个窗口,每1ms一个计数器
/*** 滑动窗口限流算法* 比如1000ms 允许通过 2 个请求** @author jeb_lin* 14:14 2023/11/19*/
public class SlidingWindowRateLimiter {private long interval; // 窗口的时间间隔private long maxRequest; // 限制的调用次数private Map<Long, AtomicLong> millToCount = null; // 假如1秒切成1000个窗口,也就是1毫秒一个计数器private LinkedList<Long> timeStampList = null; // 出现请求的那个时间戳,需要记录下来,用于后期的删除private AtomicLong counter; // 计数器public SlidingWindowRateLimiter(long interval, long maxRequest) {this.interval = interval;this.maxRequest = maxRequest;this.millToCount = new HashMap<>();this.timeStampList = new LinkedList<>();this.counter = new AtomicLong(0);}public static void main(String[] args) throws Exception {testNormal();}/*** 测试正常的情况*/private static void testNormal() {// 比如1000ms 允许通过10个请求SlidingWindowRateLimiter limiter = new SlidingWindowRateLimiter(1000, 2);for (int i = 0; i < 10; i++) {if (limiter.allow()) {System.out.println(i + " -> ok ");} else {System.out.println(i + " -> no");}}}private boolean allow() {long now = System.currentTimeMillis();// 剔除掉过期的窗口,比如现在是1001ms,那么1ms的窗口就需要剔除掉while (!timeStampList.isEmpty() && now - interval > timeStampList.getFirst()) {long timeStamp = timeStampList.poll();for (int i = 0; i < millToCount.getOrDefault(timeStamp,new AtomicLong(0)).get(); i++) {counter.decrementAndGet();}millToCount.remove(timeStamp);}if (counter.get() >= maxRequest) {return false;} else {timeStampList.add(now); // 当前出现成功请求,那么串上listAtomicLong timeStampCounter = millToCount.getOrDefault(now, new AtomicLong(0L));timeStampCounter.incrementAndGet();millToCount.put(now, timeStampCounter);counter.incrementAndGet();return true;}}
}
优点
- 简单易懂
- 精度高(通过调整时间窗口的大小来实现不同的限流效果)
缺陷
- 依旧存在临界问题,不可能无限小
- 占用更多内存空间
三、漏桶算法(固定消费速率)
漏桶限流算法(Leaky Bucket Algorithm)拥有更平滑的流量控制能力。其中漏桶是一个形象的比喻,这里可以用生产者消费者模式进行说明,请求是一个生产者,每一个请求都如一滴水,请求到来后放到一个队列(漏桶)中,而桶底有一个孔,不断的漏出水滴,就如消费者不断的在消费队列中的内容,消费的速率(漏出的速度)等于限流阈值。即假如 QPS 为 2,则每 1s / 2= 500ms 消费一次。漏桶的桶有大小,就如队列的容量,当请求堆积超过指定容量时,会触发拒绝策略。
类似于Kafka的消费者,在不调整配置的情况下,消费速度是固定的,多余的请求会积压,如果超过你kafka配置的topic最大磁盘容量,那么会丢消息。(topic是一个目录,topic下N个partition目录,假如你每个partition配置了1G的容量,那么超过这个这个容量,就会删除partition下的segement文件 xxx.index,xxx.log)
/*** 漏桶算法* 比如 1秒只能消费2个请求** @author jeb_lin* 15:55 2023/11/19*/
public class LeakyBucketRateLimiter {private int consumerRate; // 消费速度private Long interval; // 时间间隔,比如1000msprivate int bucketCapacity; // 桶的容量private AtomicLong water; // 桶里面水滴数量public LeakyBucketRateLimiter(int consumerRate, Long interval, int bucketCapacity) {this.consumerRate = consumerRate;this.interval = interval;this.bucketCapacity = bucketCapacity;this.water = new AtomicLong(0);scheduledTask();}// 周期任务,比如每1000ms消费2个请求private void scheduledTask() {ScheduledExecutorService service = Executors.newScheduledThreadPool(1);service.scheduleAtFixedRate((Runnable) () -> {for (int i = 0; i < consumerRate && water.get() > 0; i++) {this.water.decrementAndGet();}System.out.println("water -> " + this.water.get());}, 0, interval, TimeUnit.MILLISECONDS);}public static void main(String[] args) {// 1000毫秒消费2个请求LeakyBucketRateLimiter limiter = new LeakyBucketRateLimiter(2, 1000L, 10);for (int i = 0; i < 10; i++) {if (limiter.allow()) {System.out.println(i + "-> ok");} else {System.out.println(i + "-> no");}}}private boolean allow() {if (bucketCapacity < water.get() + 1) {return false;} else {water.incrementAndGet();return true;}}
}
输出:
0 -> ok
1 -> ok
2 -> no
3 -> no
4 -> no
5 -> no
6 -> no
7 -> no
8 -> no
9 -> no
优点
- 可以控制请求的处理速度,避免过载或者过度闲置,避免瞬间请求过多导致系统崩溃或者雪崩。
- 可以通过调整桶的大小和漏出速率来满足不同的限流需求(这个基本靠手动)
缺陷
- 需要对请求进行缓存,会增加服务器的内存消耗。
- 但是面对突发流量的时候,漏桶算法还是循规蹈矩地按照固定的速率处理请求。
四、令牌桶算法
令牌桶算法是一种常用的限流算法,相对于漏桶算法,它可以更好地应对突发流量和解决内存消耗的问题。该算法维护一个固定容量的令牌桶,每秒钟会向令牌桶中放入一定数量的令牌。当有请求到来时,如果令牌桶中有足够的令牌,则请求被允许通过并从令牌桶中消耗一个令牌,否则请求被拒绝。
4.2 令牌桶限流代码实现
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;/*** 令牌桶限流算法* 每 1000ms 生成2个令牌** @author jeb_lin* 14:14 2023/11/19*/
public class TokenBucketRateLimiter {private long interval; // 窗口的时间间隔private long rate; // 速率private AtomicLong tokenCounter; // 令牌的数量private final long bucketCapacity; // 桶的大小public TokenBucketRateLimiter(long interval, long rate ,long bucketCapacity) {this.interval = interval;this.rate = rate;this.tokenCounter = new AtomicLong(0);this.bucketCapacity = bucketCapacity;scheduledProduceToken();}private void scheduledProduceToken() {ExecutorService executorService = Executors.newScheduledThreadPool(1);((ScheduledExecutorService) executorService).scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {// 桶里面放不下了if(tokenCounter.get() + rate > bucketCapacity){System.out.println("bucket is full");return;}long token = tokenCounter.addAndGet(rate);System.out.println("token -> " + token);}}, 0, interval, TimeUnit.MILLISECONDS);}public static void main(String[] args) throws Exception {testNormal();}/*** 测试正常的情况*/private static void testNormal() throws Exception{// 比如1000ms 允许通过10个请求TokenBucketRateLimiter limiter = new TokenBucketRateLimiter(1000, 2,10);// 模拟瞬时流量,5秒前都没请求,5秒后瞬时流量上来了Thread.sleep(5000);for (int i = 0; i < 12; i++) {if (limiter.allow()) {System.out.println(i + " -> ok ");} else {System.out.println(i + " -> no");}}}private boolean allow() {if(tokenCounter.get() > 0){tokenCounter.getAndDecrement();return true;}return false;}
}
输出:
token -> 2
token -> 4
token -> 6
token -> 8
token -> 10
bucket is full
0 -> ok
1 -> ok
2 -> ok
3 -> ok
4 -> ok
5 -> ok
6 -> ok
7 -> ok
8 -> ok
9 -> ok
10 -> no
11 -> no
优点
Guava的RateLimiter限流组件,就是基于令牌桶算法实现的。
- 精度高:令牌桶算法可以根据实际情况动态调整生成令牌的速率,可以实现较高精度的限流。
- 弹性好:令牌桶算法可以处理突发流量,可以在短时间内提供更多的处理能力,以处理突发流量。
缺陷
- 实现复杂:在短时间内有大量请求到来时,可能会导致令牌桶中的令牌被快速消耗完,从而限流。这种情况下,可以考虑使用漏桶算法。(需要削峰填谷,学习kafka就用漏桶算法)
- 时间精度要求高:令牌桶算法需要在固定的时间间隔内生成令牌,因此要求时间精度较高,如果系统时间不准确,可能会导致限流效果不理想。
相关文章:

4种经典的限流算法
0、基础知识 1000毫秒内,允许2个请求,其他请求全部拒绝。 不拒绝就可能往db打请求,把db干爆~ interval 1000 rate 2; 一、固定窗口限流 固定窗口限流算法(Fixed Window Rate Limiting Algorithm)是…...
<MySQL> 什么是数据库事务?事务该如何使用?
目录 一、事务的概念 二、事务的核心特性 三、事务操作中的常见BUG 3.1 脏读 3.2 不可重复读 3.3 幻读 四、隔离级别 五、使用事务 一、事务的概念 “事务”是指一组操作,在逻辑上是不可分割的,组成这组操作的各个语句,或者全部执行成…...

Linux 网络:PMTUD 简介
文章目录 1. 前言2. Path MTU Discovery(PMTUD) 协议2.1 PMTUD 发现最小 MTU 的过程 3. Linux 的 PMTUD 简析3.1 创建 socket 时初始化 PMTUD 模式3.2 数据发送时 PMTUD 相关处理3.2.1 源头主机发送过程中 PMTU 处理3.2.2 转发过程中 PMTUD 处理 4. PMTUD 观察5. 参考链接 1. 前…...

BatchNormalization:解决神经网络中的内部协变量偏移问题
ICML2015 截至目前51172引 论文链接 代码连接(planing) 文章提出的问题 减少神经网络隐藏层中的”内部协变量偏移”问题。 在机器学习领域存在“协变量偏移”问题,问题的前提是我们划分数据集的时候,训练集和测试集往往假设是独立同分布(i.i.d)的,这种独立同分布更有利于…...

DAC实验(DAC 输出三角波实验)(DAC 输出正弦波实验)
DAC 输出三角波实验 本实验我们来学习使用如何让 DAC 输出三角波,DAC 初始化部分还是用 DAC 输出实验 的,所以做本实验的前提是先学习 DAC 输出实验。 使用 DAC 输出三角波,通过 KEY0/KEY1 两个按键,控制 DAC1 的通道 1 输出两种…...

许多网友可能还不知道,升级到Windows 11其实没那么复杂,只要符合几个条件可以了
如果你的Windows 10电脑可以升级Windows 11,现在怎么办?有几种方法可以免费安装新的操作系统。以下是你的选择。 如果你想升级到Windows 11,你可以随时购买一台已经安装了操作系统的新电脑。然而,如果你目前的Windows 10 PC满足所有必要的升级要求,那么在Windows 11免费的…...

ubuntu下载conda
系统:Ubuntu18.04 (1)下载安装包 wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2021.11-Linux-x86_64.sh 报错错误 403:Forbidden 解决方法 wget -U NoSuchBrowser/1.0 https://mirrors.tuna.tsingh…...

重磅 | 进一步夯实生态建设,朗思科技与阿里龙蜥完成兼容性认证
近日,北京朗思智能科技有限公司(以下简称“朗思科技”)自主研发的数字员工产品与OpenAnolis龙蜥社区龙蜥操作系统(Anolis OS)8完成兼容性认证。测试结果显示,双方产品相互兼容,功能正常…...

Qt给控件添加图片
双击qrc文件,选择下面的addFiles,将图片添加进来,然后选中图片右键Select All 设置控件字符: ui.btnSet->setText(""); 设置资源: ui.btnSet->setStyleSheet("QPushButton{background-image:…...

3.6-Dockerfile语法梳理及最佳实践
WORKDIR是设置当前docker的工作目录 ADD 和 COPY 为了将本地的一些文件添加到docker image里面,ADD 和 COPY的作用特别像,但是ADD 和 COPY还有一些区别,ADD不仅可以添加本地文件到docker里面,还可以将文件在添加到docker image里面…...

springboot集成nacos并实现自动刷新
目录 1.说明 2.示例 3.自动刷新的注意点 1.说明 springboot项目中存在好多配置文件,比如配置数据信息,redis信息等等,配置文件可以直接放在代码,也可以放在像nacos这样的组件中,实现动态的管理,修改配置…...

java面试八股文2023完整版详解110题附带答案
以下是一份Java面试八股文2023,涵盖了Java编程语言的核心概念和常用技术,帮助你更好地准备面试。 1. Java语言有哪些特点? Java语言是一种面向对象的编程语言,具有简单、面向对象、分布式、多线程、动态等优点。它是一种跨平台的…...

微服务实战系列之Token
前言 什么是“Token”? 它是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便返回给客户端;以后客户端只携带此Token请求数据即可。 简言之,Token其实就是用户身…...

DRF纯净版项目搭建和配置
一、安装模块和项目 1.安装模块 pip install django pip install djangorestframework pip install django-redis # 按需安装 2.开启项目和api (venv) PS D:\pythonProject\env_api> django-admin startproject drf . (venv) PS D:\pythonProject\env_api> python ma…...

AUTODL云服务器使用大致步骤(适合本人版)
(一)在官网上创建一个服务器 (二)远程连接指令: 改为: (三)连接后,可在中进行代码运行 输入一些指令 python .........

无需云盘,不限流量实现Zotero跨平台同步:内网穿透+私有WebDAV服务器
🔥博客主页: 小羊失眠啦. 🎥系列专栏:《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞👍收藏⭐评论✍️ 无需云盘,不限流量实现Zotero跨平台同步:内网穿透私有WebDAV服务器 文章目…...

简朴博客系统测试报告
文章目录 一. 项目简介二. 测试概要三. 测试环境四. 测试执行概况及功能测试1. 手工测试1.1 手动测试用例编写1.2 执行的部分测试用例 2. 自动化测试Selenium2.1 编写测试用例2.2 自动化测试代码 3. 测试结果 五. 发现的问题 一. 项目简介 简朴博客系统是采用前后端分离的方式…...

Qt遇到常见问题记录
1.Qt版本选择 Qt4.8.7是Qt4的终结版本,是Qt4系列版本中最稳定最经典的 (很多嵌入式板子还是用Qt4.8),其实该版本是和Qt5.5差不多时间发布的。 参考链接 Qt 5.5 Released Qt5.6.3最最后支持xp系统的长期支持版本,Q…...

pm2在Windows环境中的使用
pm2 进程管理工具可以Windows操作系统上运行,当一台Windows电脑上需要运行多个进程时,或者运维时需要运行多个进程以提供服务时。可以使用pm2,而不再是使用脚本。 1. 使用PM2管理进程 1.1. 启动PM2项目 1.1.1. 直接启动项目 参数说明&…...

使用百度翻译API或腾讯翻译API做一个小翻译工具
前言 书到用时方恨少,只能临时抱佛脚。英文pdf看不懂,压根看不懂。正好有百度翻译API和腾讯翻译API,就利用两个API自己写一个简单的翻译工具,充分利用资源,用的也放心。 前期准备 关键肯定是两大厂的翻译API&#x…...

Flutter笔记:桌面应用 窗口定制库 bitsdojo_window
Flutter笔记 桌面应用窗口管理库 bitsdojo_window 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/details/13446…...

iOS_折叠展开 FoldTextView
1. 显示效果 Test1:直接使用: Test2:在 cell 里使用: 2. 使用 2.1 直接使用 // 1.1 init view private lazy var mooFoldTextView: MOOFoldTextView {let view MOOFoldTextView(frame: .zero)view.backgroundColor .cyanvie…...

java使用 TCP 的 Socket API 实现客户端服务器通信
一:什么是 Socket(套接字) Socket 套接字是由系统提供于网络通信的技术, 是基于 TCP/IP 协议的网络通信的基本操作,要进行网络通信, 需要有一个 socket 对象, 一个 socket 对象对应着一个 socket 文件, 这个文件在 网卡上而不是硬盘上, 所以有了 sokcet…...

conda从4.12升级到最新版23.9 自动升级失败 手动升级方法
最新版conda有多线程下载,还做了一些其它易用性改动,所以决定从很老的4.12版本升级到最新版。因为版本差别过大,使用自带的conda update conda已经不起作用了。 手动升级最先想到的是把老环境全部导出为yaml文件,在新环境里全部重…...

WPF下实现拖动任意地方都可以拖动窗口
首先在xaml中添加事件 <Window PreviewMouseLeftButtonDown"Window_PreviewMouseLeftButtonDown"PreviewMouseMove"Window_PreviewMouseMove"PreviewMouseLeftButtonUp"Window_PreviewMouseLeftButtonUp"/>然后脚本输入 Point _pressedP…...

Swin Transformer
Swin Transformer 简介 下采样的层级设计,能够逐渐增大感受野。采用window进行注意力计算,极大降低了内存消耗,避免了整张图像尺寸大小的qkv矩阵滑窗操作包括不重叠的 local window,和重叠的 cross-window。不重叠的local window…...

【csapp lab】lab2_bomblab
文章目录 前言实验内容phase_1phase_2phase_3phase_4phase_5phase_6secret_phase 前言 刚做了csapp lab2,记录一下。 我这里用的的系统环境是Ubuntu22.04,是64位系统,与用32位系统可能有所差异。 实验共包括七个阶段,每个阶段考…...

开发者分享 | Ascend C算子开发及单算子调用
本文分享自《AscendC算子开发及单算子调用》,作者:goldpancake。 笔者在阅读Ascend C官方文档的过程中发现,对于初学者来说,尤其是第一次接触异构编程思想的初学者,有部分内容是无需特别关注的,例如算子工…...

如何在 Linux 上部署 RabbitMQ
如何在 Linux 上部署 RabbitMQ 文章目录 如何在 Linux 上部署 RabbitMQ安装 Erlang从预构建的二进制包安装从源代码编译 Erlang RabbitMQ 的安装使用 RabbitMQ Assistant 连接 RabbitMQ Assistant 是一款优秀的RabbitMQ 可视化管理工具,提供丰富的管理功能。下载地址…...

解决更换NodeJs版本后npm -v返回空白
一、问题描述 win11电脑上输入cmd进入控制台,输入 node --version 有正常返回安装的nodejs的版本号 再输入 npm -v 返回空白。正常情况应该是要返回版本号。 二、问题背景 最近准备学习vue,在不久前已经安装了NodeJs和python。运行了好几个开源项…...