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

news2024/11/30 1:34:26

前言

相信大家对于谷歌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.coloradmin.cn/o/1256091.html

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

相关文章

隐写-MISC-bugku-解题步骤

——CTF解题专栏—— 题目信息&#xff1a; 题目&#xff1a;隐写 作者&#xff1a;CyberFl0wer 提示&#xff1a;无 解题附件&#xff1a; 解题思路&#xff1a; 这张图片一看&#xff01;哦呦~背景还是透明的&#xff0c;那我肯定要尝试给他换换色&#xff08;不可以色色.jpg…

Java 设计模式之命令模式

命令模式 介绍 命令模式是一种行为类设计模式&#xff0c;核心是将每种请求或操作封装为一个独立的对象&#xff0c;从而可以集中管理这些请求或操作&#xff0c;比如将请求队列化依次执行、或者对操作进行记录和撤销。 命令模式通过将请求的发送者&#xff08;客户端&#x…

Django之Auth认证模块

文章目录 一、简介二、Auth模块是什么三、Auth模块常用方法create_user() 创建普通用户authenticate() 用户认证auth.login(HttpResponse&#xff0c;user)登录状态保持is_authenticated 登录认证判断auth.loginout(reqeust)退出登录login_required() 登录认证装饰器check_pass…

枚举的第一行

2023年11月26日 问题: 好奇enum的所声明的枚举类的第一行是什么 从java技术卷1中第五章5.6中,了解是枚举类的实例 验证 错误信息: 解释: 此时只有有参构造 在这个枚举类里不能使用空,大概意思是说不能使用空参创建实例 校验 在原有的基础上创建一个无参构造 结果:不再报错,第…

Adversarial Attack on Graph Structured Data(2018 PMLR)

Adversarial Attack on Graph Structured Data----《图结构数据的对抗攻击》 摘要 基于图结构的深度学习已经在各种应用中显示出令人兴奋的结果。然而&#xff0c;与图像或文本对抗攻击和防御的大量研究工作相比&#xff0c;此类模型的鲁棒性却很少受到关注。在本文中&#xf…

【数据中台】开源项目(2)-Moonbox计算服务平台

Moonbox是一个DVtaaS&#xff08;Data Virtualization as a Service&#xff09;平台解决方案。 Moonbox基于数据虚拟化设计思想&#xff0c;致力于提供批量计算服务解决方案。Moonbox负责屏蔽底层数据源的物理和使用细节&#xff0c;为用户带来虚拟数据库般使用体验&#xff0…

Python 进阶(十):数学计算(math 模块)

《Python入门核心技术》专栏总目录・点这里 文章目录 1. 导入math模块2. 常用数学函数3. 常量4. 其他函数和用法5. 总结 大家好&#xff0c;我是水滴~~ Python的math模块提供了许多数学运算函数&#xff0c;为开发者在数值计算和数据处理方面提供了强大的工具。本教程将详细介…

【python】Python将100个PDF文件对应的json文件存储到MySql数据库(源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

[AutoSAR存储] 车载存储层次 和 常用存储芯片概述

公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《AutoSAR存储》 <<<< 返回总目录 <<<< 1 存储系统层次 先抛个问题&#xff0c; 为什么要划分存储器的层次&#xff1f; 速度越快&#xff0c;但成本越高&#xff0c;从经济的角度规…

基本数据结构二叉树(1)

目录 1.树概念及结构 1.1树的概念 1.2 树的相关概念 1.3 树的表示 1.4 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 2.二叉树概念及结构 2.1概念 2.2现实中的二叉树&#xff1a; 2.3 特殊的二叉树&#xff1a; 2.5 二叉树的存储结构 2. 链式存…

leetcode面试经典150题——32 串联所有单词的子串(中等+困难)

题目&#xff1a; 串联所有单词的子串(1中等) 描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&…

MySQL基本SQL语句(上)

MySQL基本SQL语句&#xff08;上&#xff09; 一、客户端工具的使用 1、客户端工具mysql使用 mysql: mysql命令行工具&#xff0c;一般用来连接访问mysql数据库 选项说明-u, --username指定登录用户名-p, --password指定登录密码(注意是小写p),一定要放到最后面-h, --hostn…

【Spring】Spring事务失效问题

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

我的第一次SACC之旅

今年有很多第一次&#xff0c;第一次作为“游客”参加DTCC&#xff08;中国数据库大会&#xff09;&#xff0c;第一次作为讲师参与ACDU中国行&#xff08;成都站&#xff09;&#xff0c;第一次参加OB年度发布会&#xff08;包含DBA老友会&#xff09;&#xff0c;而这次是第一…

Android获取原始图片Bitmap的宽高大小尺寸,Kotlin

Android获取原始图片Bitmap的宽高大小尺寸&#xff0c;Kotlin val options BitmapFactory.Options()options.inJustDecodeBounds trueval decodeBmp BitmapFactory.decodeResource(resources, R.mipmap.p1, options)//此时&#xff0c;decode出来的decodeBmp宽高并不是原始图…

NX二次开发UF_CURVE_ask_spline_feature 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_spline_feature Defined in: uf_curve.h int UF_CURVE_ask_spline_feature(tag_t feature_id, tag_t * spline ) overview 概述 Inquire a general spline feature. …

如何深刻理解从二项式分布到泊松分布

泊松镇贴 二项分布和泊松分布的表达式 二项分布&#xff1a; P ( x k ) C n k p k ( 1 − p ) n − k P(xk) C_n^kp^k(1-p)^{n-k} P(xk)Cnk​pk(1−p)n−k 泊松分布&#xff1a; P ( x k ) λ k k ! e − λ P(xk) \frac{\lambda^k}{k!}e^{-\lambda} P(xk)k!λk​e−…

18. Python 数据处理之 Numpy

目录 1. 简介2. 安装和导入Numpy3. ndarray 对象4. 基本运算5. 索引、切片和迭代6. 条件和布尔数组7. 变换形状8. 操作数组 1. 简介 数据分析的流程概括起来主要是&#xff1a;读写、处理计算、分析建模和可视化4个部分。 Numpy 是Python 进行科学计算&#xff0c;数据分析时…

【咕咕送书 | 第六期】深入浅出阐述嵌入式虚拟机原理,实现“小而能”嵌入式虚拟机!

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《粉丝福利》 《linux深造日志》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 ⛳️ 写在前面参与规则引言一、为什么嵌入式系统需要虚拟化技术&#xff1f;1.1 专家推荐 二、本书适合谁&#x…

【Vue】@keyup.enter @v-model.trim的用法

目录 keyup.enter v-model.trim 情景一&#xff1a; 情景二&#xff1a; keyup.enter 作用&#xff1a;监听键盘回车事件 上一篇内容&#xff1a; 记事本 https://blog.csdn.net/m0_67930426/article/details/134630834?spm1001.2014.3001.5502 这里有个添加任务的功能&…