从源码重新真正认识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超标,存在造成系统崩溃风险,这里我们主要了解其设计原理,放才能在合适场景合理使用以及规避风险。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Codeforces Round 911 (Div. 2)(C~E)(DFS、数论(容斥)、SCC缩点 + DAG图上DP)

​​​​​​1900C - Anjis Binary Tree 题意&#xff1a; 凯克西奇一直被安吉冷落。通过一个共同的朋友&#xff0c;他发现安吉非常喜欢二叉树&#xff0c;于是决定解决她的问题&#xff0c;以引起她的注意。Anji 给了 Keksic 一棵有 n个顶点的二叉树。顶点 1 是根&#xff…

[Spring] 字节一面~Spring 如何解决循环依赖问题 以及 @resource 与 @autowire 同时存在时谁生效

文章目录 Spring 如何解决循环依赖问题resource 与 autowire 同时存在时谁生效 Spring 如何解决循环依赖问题 Spring在实例化一个bean的时候&#xff0c;是首先递归实例化其所依赖的所有bean&#xff0c;直到某个bean没有依赖其他bean&#xff0c;此时就会将该实例返回&#x…

基于Java SSM框架+Vue实现大学生兼职信息网站项目【项目源码+论文说明】

基于java的SSM框架Vue实现大学生兼职信息网站演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认…

【GD32307E-START】开发板开箱、开发环境建立及工程模板测试

01-GD32F307E-START开箱、开发环境建立及工程模板测试&#xff08;Keil-MDK GCC Template&#xff09; 兆易GD32307E-START开发板搭载GD32 ARM Cortex-M4微控制器主流芯片GD32F307。 开箱 板子的做工还是非常精良小巧的。有两颗按键&#xff0c;一颗是复位&#xff0c;一颗是…

时间序列预测 — LSTM实现单变量风电滚动预测(Keras)

目录 1 数据处理 1.1 数据集简介 1.2 数据集处理 2 模型训练与预测 2.1 模型训练 2.2 模型滚动预测 2.3 结果可视化 1 数据处理 1.1 数据集简介 实验数据集采用数据集5&#xff1a;风电机组运行数据集&#xff08;下载链接&#xff09;&#xff0c;包括风速、风向、温…

纯新手发布鸿蒙的第一个java应用

第一个java开发鸿蒙应用 1.下载和安装华为自己的app开发软件DevEco Studio HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 2.打开IDE新建工程&#xff08;当前用的IDEA 3.1.1 Release&#xff09; 选择第一个&#xff0c;其他的默认只能用(API9)版本&#xff0c;…

居家适老化设计第三十二条---卫生间之扶手

以上产品图片均来源于淘宝 侵权联系删除 居家适老化中的扶手是指在家居环境中&#xff0c;为老年人提供支撑和帮助的装置&#xff0c;通常安装在家中的各个需要扶抓的位置&#xff0c;如楼梯、卫生间、浴室、厨房等处。扶手的设计应考虑老年人的体力、平衡和安全需求&#xf…

顶级Mac数据恢复工具—— 13个 Mac 数据恢复程序榜单

如果您点击此博客&#xff0c;首先可能是您不小心格式化了外部或内部存储&#xff0c;无论是 SD卡还是硬盘&#xff0c;其次&#xff0c;您收到了一些错误消息&#xff0c;表明您丢失了所有内容&#xff0c;现在您已经精疲力竭了的形状。原因可能有多种&#xff1a;您不小心删除…

探索 Linux vim/vi 编辑器:介绍、模式以及基本操作演示

&#x1f490;作者&#xff1a;insist-- &#x1f490;个人主页&#xff1a;insist-- 的个人主页 理想主义的花&#xff0c;最终会盛开在浪漫主义的土壤里&#xff0c;我们的热情永远不会熄灭&#xff0c;在现实平凡中&#xff0c;我们终将上岸&#xff0c;阳光万里 ❤️欢迎点…

Slf4j使用Logback时,Logback如何初始化

前言 Slf4j SLF4J&#xff0c;全称 Simple Logging Facade for Java&#xff0c;是一个用于Java编程语言的日志系统抽象层。它为多种现有日志框架&#xff08;例如Log4j、java.util.logging等&#xff09;提供了统一的接口, 但自身并不实现日志功能。 SLF4J 允许用户在部署时…

校园导游程序及通信线路设计(结尾附着总源码)

校园导游程序及通信线路设计 摘  要 新生或来访客人刚到校园&#xff0c;对校园的环境不熟悉。就需要一个导游介绍景点&#xff0c;推荐到下一个景点的最佳路径等。随着科技的发展&#xff0c;社会的进步&#xff0c;人们对便捷的追求也越来越高。为了减少人力和时间。针对对…

一起学docker系列之十一使用 Docker 安装 Redis 并配置持久化存储

目录 前言1 基本安装步骤安装Redis镜像&#xff1a;查看已下载的Redis镜像&#xff1a;运行Redis容器&#xff1a;进入Redis容器&#xff1a;使用Redis CLI进行基本操作&#xff1a; 2 配置文件同步准备配置文件&#xff1a;修改Redis配置文件 /app/redis/redis.conf&#xff1…

Project DESFT 白皮书中文版——应用于普惠金融的可信数字凭证解决方案

1. 概述 Project DESFT 是由 Solv 基金会与 zCloak Network 联合设计孵化&#xff0c;以跨境贸易和金融服务为场景的分布式可信数字凭证解决方案&#xff08;Distributed Trusted Digital Credential Solution&#xff09;&#xff0c;项目获得新加坡金管局&#xff08;Monetar…

项目实战——苍穹外卖(DAY10)

如果之前有改过端口号造成WebSocket无法连接的&#xff0c;可以看本篇文章“来单提醒”前的内容进行解决。 课程内容 Spring Task 订单状态定时处理 WebSocket 来单提醒 客户催单 功能实现&#xff1a;订单状态定时处理、来单提醒和客户催单 订单状态定时处理&#xff1a…

Python---练习:使用Python函数编写通讯录系统

预览通讯录系统最终效果 首先&#xff0c;进行需求分析&#xff0c;整个系统功能&#xff0c;分为6个板块&#xff0c;功能如下&#xff1a; ① 添加学员信息 ② 删除学员信息 ③ 修改学员信息 ④ 查询学员信息 ⑤ 遍历所有学员信息 ⑥ 退出系统 系统共6个功能&#xff…

drool 7 multiThread 测试

基本信息 通过option &#xff0c;使用如下代码进行设置 //线程数量10MaxThreadsOption optionMaxThreadsOption.get(10);kieBaseConf.setOption(option);kieBaseConf.setOption(MultithreadEvaluationOption.YES);并发是以CompositeDefaultAgenda/Rule为颗粒度来的&#xff0…

国企央企降薪20%,年终奖也没了。

* 你好&#xff0c;我是前端队长&#xff0c;在职场&#xff0c;玩副业&#xff0c;文末有福利&#xff01; 精彩回顾&#xff1a;京东内部员工&#xff0c;爆料工资与公积金收入&#xff01; 最近&#xff0c;很多国企央企也开始降薪了&#xff0c;有的甚至降幅达到 21%&#…

针对操作系统漏洞的反馈方法

一、针对操作系统漏洞的反馈方法 漏洞扫描指基于漏洞数据库&#xff0c;通过扫描等手段对指定的远程或者本地计算机系统的安全脆弱性进行检测&#xff0c;发现可利用漏洞的一种安全检测&#xff08;渗透攻击&#xff09;行为。在进行漏洞扫描后&#xff0c;需先确定哪些是业务…

2023年亚太杯数学建模C题新能源汽车成品文章(思路模型代码成品)

一、翻译 新能源汽车是指采用先进的技术原理、新技术和新结构&#xff0c;以非常规车用燃料&#xff08;非常规车用燃料是指汽油和柴油以外的燃料(非常规车用燃料是指汽油和柴油以外的燃料&#xff09;&#xff0c;并集成了汽车动力控制和驱动等先进技术的汽车。新能源汽车包括…

博物馆线上导览系统的设计与实现-计算机毕业设计源码64574

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…