从源码重新真正认识RateLimiter(SmoothBursty实现)

前言

相信大家对于谷歌RateLimiter一定并不陌生,在项目中应该也经常拿来进行限流,但是对于其实现原理并不一定能用熟于心,本文带大家从源码探究RateLimiter的设计与具体实现。

RateLimiter的组成

image.png
从源码可以看到,RateLimiter由stopwatch与mutexDoNotUseDirectly组成,先简单了解其分别的作用如下:

  • stopwatch:计时器的作用.
  • mutexDoNotUseDirectly:主要通过锁解决并发问题,本文暂不叙述此块,感兴趣的同学可以留言。

源码分析

image.png
image.png

上图展示了RateLimite的类图,可以看到其有一个子类SmoothRateLimiter,在往下看SmoothRateLimiter中有两个实现类SmoothBursty与SmoothWarmingUp,分别表示两种限流的不同场景,SmoothBursty是我们常见的关于限流算法中令牌桶算法的一个实现,通过固定速率生成令牌,当流量进入时,申请令牌,令牌充足时则直接获取成功,不充足时返回等待时间,而SmoothWarmingUp与SmoothBursty不同的是,SmoothWarmingUp在固定速度的基础上增加了预热流程,可以更好的应对突发流量。 另外,在初始化和小流量时更慢得进行流量得提供也符合实际的应用场景,本文主要讲述常用SmoothBursty的实现。

为了便于理解,我们从最简单限流程序开始一步一部理解RateLimiter的设计与实现。
image.png

1.RateLimiter创建

image.png
可以看到,RateLimiter创建分为两步,首先创建RateLimiter的实现类SmoothBursty对象,然后setRate设置限流器的控制速率。
首先我们看下SmoothBursty的实现,其首先创建了SleepingStopwatch类stopwatch对象。

image.png
image.png

stopwatch中初始elapsedNanos = 0 startTick = 765333275998400,其有一个sleepMicrosUninterruptibly方法,释义如下

  • elapsedNanos:经过的时间,单位为纳秒
  • startTick:开始时间
  • sleepMicrosUninterruptibly(long micros):实现了不可中断的不可中断的sleep(用于令牌不足时限流等待)

紧接着其传入stopwatch与maxBurstSeconds创建一个SmoothBursty对象,SmoothBursty继承至SmoothRateLimiter,其额外定义了一个maxBurstSeconds变量,SmoothRateLimiter继承至RateLimiter,是RateLimiter抽象类的具体实现,其中有四个变量我们同maxBurstSeconds一起进行解释:

  • SmoothRateLimiter.storedPermits:实际预存的许可(即令牌)
  • SmoothRateLimiter.maxPermits:最大的许可数(即令牌)
  • SmoothRateLimiter.stableIntervalMicros:每产生一个令牌需要消耗的微秒数
  • SmoothRateLimiter.nextFreeTicketMicros:初始值为0L,表示下一个令牌可用的时间戳
  • SmoothBursty.maxBurstSeconds:初始值为1.0D,表示桶中最多可以保存多少秒存入的令牌数

image.png
从上图可以看到RateLimiter创建后,开始setRate传入的permitsPerSecond设置限流速率

  • permitsPerSecond:令牌数/每秒钟,即我们期望限制的qps.

image.png

传入的nowMicros当前值为1030409545,nextFreeTicketMicros初始为0,此步骤计算赋值了当前存储的令牌数量storedPermits与
nextFreeTicketMicros = nowMicros(1030409545);

image.png

然后根据传入的permitsPerSecond设置了产生一枚令牌需要的时间:stableIntervalMicros。

image.png

接着起开始按比例更新当前存储的令牌数量,可以看到,初始令牌数量为0时,其首次创建时存储的令牌数量即为0.0, maxPermits = maxBurstSeconds * permitsPerSecond = 1.0
至此RateLimiter的初始创建结束,下面我们从最简单也是日常使用最多的方法acquire()看看其如何通过上述各个变量控制限流。为了方便理解,下图展示了,当前RateLimiter的组成。

image.png

2.RateLimiter.acquire()控制限流

image.png

还是从源码入手,可以看到acquire()实际调用的方法acquire(1),即当前需要获取1块令牌,其实现分为3个步骤

  1. 计算需要等待的时间microsToWait
  2. stopwatch.sleepMicrosUninterruptibly(microsToWait)进行阻塞等待。
  3. 返回等待时间
    从第1步开始看,传入令牌数与当前时间nowMicros = 2322440641 由于当前令牌数量足够计算出momentAvailable = 0,即无需阻塞等待,

image.png

且将nextFreeTicketMicro等于当前时间nowMicros后,则可以推测出下次计算时nowMicros必然大于nextFreeTicketMicros,此时无需等待。

这是因为nowMicros > nextFreeTicketMicros 时,此间产生令牌数量 + 当前持有的令牌数量 一定大于 最大的令牌数量,而最大的令牌数量大于请求的令牌数量,所以请求无需限流阻塞等待。

而当并发请求数变多,导致某一时刻持有的令牌数量不足时,则会发生限流阻塞等待,为了方便分析,我们通过限流设置为1qps,通过单词请求1000000000个令牌数模拟高并发场景。

image.png

可以看到初次请求时,nowMicros > nextFreeTicketMicros满足,此时令牌数量为1,而超过的令牌数量为refreshPermits = 99999999,通过计算,需要99999999000000的间隔时间才能产生这么多令牌,而此时注意了,返回值为上一次(这里初始请求的上一次即初始值为0)的nextFreeTicketMicros = 0,然后将本次请求的nextFreeTicketMicros增加99999999000000,而最终计算出的等待时间为Math.max(momentAvailable - nowMicros, 0L) = 0.实际并没有产生等待,而在下一次请求时,如下图可以看到此时nowMicros < nextFreeTicketMicros, 此时说明还未到令牌释放的时间,需要等待Math.max(momentAvailable - nowMicros, 0L) = 0, 可以看到本次请求等待的时间,其实时上次请求时超出的令牌数需要等待的时间,继续往下执行你就会发现,RateLimiter每次请求的超支的令牌等待时间,都是在下一次执行时进行等待。

image.png

结语

SmoothBrusty的设计遵循令牌桶的思路,SmoothBursty以指定的速率生成许可,当一个请求申请获取许可时,如果当前许可数满足申请数量,则消耗掉许可,直接返回无需等待。当当前许可数小于申请数量时,会计算多余部分许可需要等待生成的时间,更新下一次许可可发放的时间,但值得注意的是尽管已经消耗掉所有的许可并且不够总请求数量,本次请求也并不会阻塞等待,而是将阻塞等待放到下一个请求,说明此处可以支持突发流量。这里的设计其实还是蛮巧妙的。比如一些突发流量场景,当前瞬发的高流量请求可快速返回,无需阻塞,而后续请求可能相隔很久,则请求时不会等待,从而提高系统整体的响应速率,当然这在某些场景可能会导致qps超标,存在造成系统崩溃风险,这里我们主要了解其设计原理,放才能在合适场景合理使用以及规避风险。

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

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

相关文章

Elasticsearch集群部署,配置head监控插件

Elasticsearch是一个开源搜索引擎&#xff0c;基于Lucene搜索库构建&#xff0c;被广泛应用于全文搜索、地理位置搜索、日志处理、商业分析等领域。它采用分布式架构&#xff0c;可以处理大规模数据集和支持高并发访问。Elasticsearch提供了一个简单而强大的API&#xff0c;可以…

全球SAR卫星大盘点与回波数据处理专栏目录

近年来&#xff0c;随着商业航天的蓬勃发展&#xff0c;商业SAR卫星星座成为美欧等主要航天国家的发展重点&#xff0c;目前已在全球范围内涌现出众多初创公司进军商业SAR领域&#xff0c;开始构建大规模商业微小SAR卫星星座&#xff0c;其所具有的创新服务能力将为传统的商业遥…

uniapp IOS从打包到上架流程(详细简单)

​ uniapp IOS从打包到上架流程&#xff08;详细简单&#xff09; 原创 1.登入苹果开发者网站&#xff0c;打开App Store Connect ​ 2.新App的创建 点击我的App可以进入App管理界面&#xff0c;在右上角点击➕新建App 即可创建新的App&#xff0c;如下图&#xff1a; ​ 3.…

VUE简易计划清单

目录 效果预览图 完整代码 效果预览图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>…

基于YOLOv5的视频计数 — 汽车计数实现

在视频中计数对象可能看起来有挑战性&#xff0c;但借助Python和OpenCV的强大功能&#xff0c;变得令人意外地易于实现。在本文中&#xff0c;我们将探讨如何使用YOLO&#xff08;You Only Look Once&#xff09;目标检测模型在视频流或文件中计数对象。我们将该过程分解为简单…

带你用uniapp从零开发一个仿小米商场_9. 轮播图组件封装及使用

导航栏有了,接下来就是轮播图了,轮播图如下, 因为uniapp 官方自己有轮播图,所以这里就不自己写了,直接使用uniapp的轮播图二次开发就好 uniapp的轮播图组件叫swiper ,感兴趣的朋友可以点击链接,直接去看官方文档,也可以看我这里实操 用hbuilderX编译uniapp的代码有一个好处…

C语言之内存函数

C语言之内存函数 文章目录 C语言之内存函数1. memcpy 使⽤和模拟实现1.1 memcpy 函数的使用1.3 memcpy的模拟实现 2. memmove 使⽤和模拟实现2.1 memmove 函数的使用2.2 memmove的模拟实现 3. memset 函数的使用4. memcmp 函数的使⽤ 1. memcpy 使⽤和模拟实现 函数声明如下&a…

《已解决: ImportError: Keras requires TensorFlow 2.2 or higher 问题》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…

qt-C++笔记之不使用ui文件纯C++构建时控件在布局管理器作用下的默认位置和大小实践

qt-C笔记之不使用ui文件纯C构建时控件在布局管理器作用下的默认位置和大小实践 code review! 文章目录 qt-C笔记之不使用ui文件纯C构建时控件在布局管理器作用下的默认位置和大小实践1.ChatGPT解释2.ChatGPT——resize()和move()详解3.默认大小和位置——示例运行一4.默认大小…

excel表中慎用合并单元格,多用跨列居中

如下一个excel例表&#xff1a; 要将首行居中&#xff0c;最好的办法如下&#xff1a; 1、选中首行单元格 2、按下ctrl1&#xff0c;调出“设置单元格格式”&#xff0c;选中“对齐”&#xff0c;在“水平对齐”中选择“跨列居中” 3、完成任务 这样居中的好处是&#xff1a;可…

【NeRF】3、MobileR2L | 移动端实时的神经光场(CVPR2023)

论文&#xff1a;Real-Time Neural Light Field on Mobile Devices 代码&#xff1a;https://github.com/snap-research/MobileR2L 出处&#xff1a;CVPR2023 贡献&#xff1a; 设计了一套移动端实时的 R2L 网络结构 MobileR2L&#xff0c;在 iphone13 上渲染一张 1008x756…

RC-MVSNet:无监督的多视角立体视觉与神经渲染--论文笔记(2022年)

RC-MVSNet&#xff1a;无监督的多视角立体视觉与神经渲染--论文笔记&#xff08;2022年&#xff09; 摘要1 引言2 相关工作2.1 基于监督的MVS2.2 无监督和自监督MVS2.3 多视图神经渲染 3 实现方法3.1 无监督的MVS网络 Chang, D. et al. (2022). RC-MVSNet: Unsupervised Multi-…

帮管客CRM SQL注入漏洞复现

0x01 产品简介 帮管客CRM是一款集客户档案、销售记录、业务往来等功能于一体的客户管理系统。帮管客CRM客户管理系统&#xff0c;客户管理&#xff0c;从未如此简单&#xff0c;一个平台满足企业全方位的销售跟进、智能化服务管理、高效的沟通协同、图表化数据分析帮管客颠覆传…

【深度学习实验】图像处理(二):PIL 和 PyTorch(transforms)中的图像处理与随机图片增强

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 导入需要的工具包1. PIL图像处理a. 生成绿色和蓝色图像b. 缩放和合成图像c 在合成图像上添加文字d. 展示并保存图像 2. PIL随机图像增强a. 定义随机图像增强函数b. 实验结果展示 3. PyTorch&…

redis实现消息延迟队列

业务场景 在很多软件系统功能中都会出现定时任务的业务场景,比如提前点单,比如定时发布动态,文章等而出现这样的的定时的任务为延迟队任务 代码模块 任务的持久化一般都需要建立一个任务表和任务日志表,避免宕机导致任务失效,先新建立一个数据库,创建基本的任务表和任务日志表…

【MOJO】Modular语言安装和测试

目录 一、Mojo介绍 Linux​ Mac 二、安装Mojo SDK 三、mojo代码测试 3.1、在 REPL 中运行代码​ 3.2、构建并运行 Mojo 源文件​ 运行mojo文件​ 构建可执行二进制文件​ 四、VSCode安装 一、Mojo介绍 在学习Rust语言的过程中无意发现了Modular语言&#xff0c;语言…

nginx基础篇学习

一、nginx编译安装 1、前往nginx官网获取安装包 下载安装包 2、解压 3、安装 进入安装包 安装准备&#xff1a;nginx的rewrite module重写模块依赖于pcre、pcre-devel、zlib和zlib-devel库&#xff0c;要先安装这些库 安装&#xff1a; 编译&#xff1a; 启动&#xff…

web前端之vue和echarts的堆叠柱状图顶部显示总数、鼠标悬浮工具提示、设置图例的显示与隐藏、label、legend、tooltip

MENU 效果图htmlJavaScripstyle解析 效果图 html <template><div><div><div id"idStackedColumnChart" style"width: 100%; height: 680px"></div></div></div> </template>JavaScrip export default {…

python-opencv轮廓检测(外轮廓检测和全部轮廓检测,计算轮廓面积和周长)

python-opencv轮廓检测&#xff08;外轮廓检测和全部轮廓检测&#xff0c;计算轮廓面积和周长&#xff09; 通过cv2.findContours&#xff0c;我们可以进行轮廓检测&#xff0c;当然也有很多检测模式&#xff0c;我们可以通过选择检测模式&#xff0c;进行外轮廓检测&#xff…

通过ros系统中websocket中发送sensor_msgs::Image数据给web端显示(二)

通过ros系统中websocket中发送sensor_msgs::Image数据给web端显示(二) mp4媒体流数据 #include <ros/ros.h> #include <signal.h> #include <sensor_msgs/Image.h> #include <message_filters/subscriber.h> #include <message_filters/synchroniz…