【Java难点攻克】「Guava RateLimiter」针对于限流器的入门到实战和源码原理分析

news2024/9/28 7:20:27

限流器的思路和算法

如果让你来造一个限流器,有啥想法?

漏桶算法

  • 用一个固定大小的队列。比如设置限流为5qps,1s可以接受5个请求;那我们就造一个大小为5的队列,如果队列为满了,就拒绝请求;如果队列未满,就往队列添加请求。

令牌算法

令牌听起来挺酷的。以固定的速率往桶里发放令牌。然后消费者每次要取到令牌(acquire)才可以响应请求。

由于令牌是固定间隔发放的,假设还是5qps,如果我有1s内没有请求,我的令牌桶就满了,可以一瞬间响应5个请求(一次过取5个令牌),也就是可以应对瞬时流量。

那么这里也涉及一个固定间隔发放的问题,难道也是需要定时任务往”桶里“放令牌吗?

那我们来看下Guava怎么搞的,假设限流为2qps,那么固定发放令牌的时间stableIntervalMicros就是500ms,初始化的storedPermits当前桶里的令牌数是0。

RateLimiter限流器

RateLimiter从概念上来讲,速率限制器会在可配置的速率下分配许可证。如果必要的话,每个acquire() 会阻塞当前线程直到许可证可用后获取该许可证。一旦获取到许可证,不需要再释放许可证。

RateLimiter使用的是一种叫令牌桶的流控算法,RateLimiter会按照一定的频率往桶里扔令牌,线程拿到令牌才能执行,比如:你希望自己的应用程序QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里扔1000个令牌。

com.google.common.util.concurrent.RateLimiter
@ThreadSafe
@Beta
public abstract class RateLimiter extends Object

RateLimiter的作用

RateLimiter经常用于限制对一些物理资源或者逻辑资源的访问速率。与Semaphore 相比,Semaphore 限制了并发访问的数量而不是使用速率。(注意尽管并发性和速率是紧密相关的,比如参考Little定律)

通过设置许可证的速率来定义RateLimiter。在默认配置下,许可证会在固定的速率下被分配,速率单位是每秒多少个许可证。为了确保维护配置的速率,许可会被平稳地分配,许可之间的延迟会做调整。

可能存在配置一个拥有预热期的RateLimiter 的情况,在这段时间内,每秒分配的许可数会稳定地增长直到达到稳定的速率。

举例来说明如何使用RateLimiter,想象下我们需要处理一个任务列表,但我们不希望每秒的任务提交超过两个:

//速率是每秒两个许可
final RateLimiter rateLimiter = RateLimiter.create(2.0);
void submitTasks(List tasks, Executor executor) {
    for (Runnable task : tasks) {
        rateLimiter.acquire(); // 也许需要等待
        executor.execute(task);
    }
}

再举另外一个例子,想象下我们制造了一个数据流,并希望以每秒5kb的速率处理它。可以通过要求每个字节代表一个许可,然后指定每秒5000个许可来完成:

// 每秒5000个许可
final RateLimiter rateLimiter = RateLimiter.create(5000.0);
void submitPacket(byte[] packet) {
    rateLimiter.acquire(packet.length);
    networkService.send(packet);
}

有一点很重要,那就是请求的许可数从来不会影响到请求本身的限制(调用acquire(1) 和调用acquire(1000) 将得到相同的限制效果,如果存在这样的调用的话),但会影响下一次请求的限制。

如果一个高开销的任务抵达一个空闲的RateLimiter,它会被马上许可,但是下一个请求会经历额外的限制,从而来偿付高开销任务。注意:RateLimiter并不提供公平性的保证。

create方法

  • create(double permitsPerSecond, long warmupPeriod, TimeUnit unit):根据指定的稳定吞吐率和预热期来创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少个请求量),在这段预热时间内,RateLimiter每秒分配的许可数会平稳地增长直到预热期结束时达到其最大速率。(只要存在足够请求数来使其饱和)
  • create(double permitsPerSecond):根据指定的稳定吞吐率创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少查询)

acquire方法

  • acquire():从RateLimiter获取一个许可,该方法会被阻塞直到获取到请求
public double acquire()

从RateLimiter获取一个许可,该方法会被阻塞直到获取到请求。如果存在等待的情况的话,告诉调用者获取到该请求所需要的睡眠时间。该方法等同于acquire(1)。

  • 返回值

执行速率的所需要的睡眠时间,单位为妙;如果没有则返回0

  • acquire(int permits):从RateLimiter获取指定许可数,该方法会被阻塞直到获取到请求。
public double acquire(int permits)

从RateLimiter获取指定许可数,该方法会被阻塞直到获取到请求数。如果存在等待的情况的话,告诉调用者获取到这些请求数所需要的睡眠时间。

  • 参数:permits – 需要获取的许可数
  • 返回:执行速率的所需要的睡眠时间,单位为妙;如果没有则返回0
  • 抛出:IllegalArgumentException – 如果请求的许可数为负数或者为0

tryAcquire方法

  • tryAcquire():从RateLimiter 获取许可,如果该许可可以在无延迟下的情况下立即获取得到的话

  • tryAcquire(int permits):从RateLimiter 获取许可数,如果该许可数可以在无延迟下的情况下立即获取得到的话

  • tryAcquire(int permits, long timeout, TimeUnit unit):从RateLimiter 获取指定许可数如果该许可数可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可数的话,那么立即返回false (无需等待)

  • tryAcquire(long timeout, TimeUnit unit):从RateLimiter 获取许可如果该许可可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待)

详细分析

public static RateLimiter create(double permitsPerSecond)

根据指定的稳定吞吐率创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少查询)。

返回的RateLimiter 确保了在平均情况下,每秒发布的许可数不会超过permitsPerSecond,每秒钟会持续发送请求。

当传入请求速率超过permitsPerSecond,速率限制器会每秒释放一个许可(1.0 / permitsPerSecond 这里是指设定了permitsPerSecond为1.0) 。

当速率限制器闲置时,允许许可数暴增到permitsPerSecond,随后的请求会被平滑地限制在稳定速率permitsPerSecond中。

参数

  • permitsPerSecond – 返回的RateLimiter的速率,意味着每秒有多少个许可变成有效。

抛出异常

  • IllegalArgumentException – 如果permitsPerSecond为负数或者为0
public static RateLimiter create(double permitsPerSecond,long warmupPeriod,TimeUnit unit)

根据指定的稳定吞吐率和预热期来创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少查询),在这段预热时间内,RateLimiter每秒分配的许可数会平稳地增长直到预热期结束时达到其最大速率(只要存在足够请求数来使其饱和)。

同样地,如果RateLimiter 在warmupPeriod时间内闲置不用,它将会逐步地返回冷却状态。

它会像它第一次被创建般经历同样的预热期。返回的RateLimiter 主要用于那些需要预热期的资源,这些资源实际上满足了请求(比如一个远程服务),而不是在稳定(最大)的速率下可以立即被访问的资源。返回的RateLimiter 在冷却状态下启动(即预热期将会紧跟着发生),并且如果被长期闲置不用,它将回到冷却状态。

参数

  • permitsPerSecond – 返回的RateLimiter的速率,意味着每秒有多少个许可变成有效。
  • warmupPeriod – 在这段时间内RateLimiter会增加它的速率,在抵达它的稳定速率或者最大速率之前
  • unit – 参数warmupPeriod 的时间单位

抛出异常

  • IllegalArgumentException – 如果permitsPerSecond为负数或者为0

实践案例

第1次获取10个令牌
  • nowMicro是刚开始运行的时间,是一个很小的数,约等于0;

  • resync(nowMicro),更新令牌数,由于nowMicro约等于0,其实令牌数不会更新((0-0)/5000 = 0),令牌数还是0(约等于0)

  • storedPermitsToSpend,其实当前并没有令牌,所以取min,约等于0;

  • freshPermits,需要预支付10个令牌,约等于10;

  • 预支付之后需要等待10 * interval = 10 * 500 ,约等于5000ms,5000000微秒

  • this.nextFreeTicketMicros 需要加上 waitMicros 也就是 下一次可以获得令牌的时间是5000ms之后。

  • 所以我们看到输出信息的第一行在第0s获取了10个令牌之后,下一次再想获取1个令牌需要等待5000ms也就是5s。

第2次获取1个令牌

然后再一次想获取1个令牌,当前时间还是约等于0,这时候resync,nowMicros(0)比nextFreeTicketMicros(5000)小,令牌不更新。

returnValue=5000,storedPermitsToSpend=0,freshPermits=1,需要再等 waitMicros=1 * 500ms,然后nextFreeTicketMicros更新为5000+500=5500,返回returnValue=5000;外层函数睡眠5000ms,返回5000(输出打印获取1个token,约5s)

第3次获取10个令牌

上面说的,睡了5000ms,当前时间nowMicros=5000;

resync,nowMicros(5000)比nextFreeTicketMicros(5500)小,令牌不更新,还是欠费状态,只能预支付。

returnValue=5500, storedPermitsToSpend=0,freshPermits=10,需要预支付10个令牌, waitMicros=10 * 500ms = 5000,然后nextFreeTicketMicros更新为5500+5000=10500,返回returnValue=5500;外层函数睡眠5500-5000=500ms,返回500(输出打印获取10个token,约0.5s)

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

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

相关文章

JRebelXRebel的配置和使用(进阶篇)

JRebel&XRebel的配置和使用嘚吧嘚设置JRebel快捷键XRebel使用嘚吧嘚 之前简单介绍了JRebel&XRebel的安装和使用,不了解的朋友可以补补课😆。 JRebel&XRebel这款插件不仅仅可以用来热部署,所以继续分享一下这款插件的相关使用&a…

12月2日(第四天)

使用myabtis自动生成的时候&#xff0c;发现xml文件只会merge不会覆盖&#xff0c;这时候需要使用插件&#xff1a; <plugin type"org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />MyBatis Generator配置文件context元素的defaultModelType属性…

封装window10-21H1踩的坑,无法分析或处理pass[specialize]应答文件

最近在研究封装镜像&#xff0c;无奈公司不给用win11&#xff0c;只能封装win10 2022年全新Windows11系统封装图文教程&#xff08;一&#xff09;定制母盘 - 小鱼儿yr系统 (yrxitong.com) 坑1&#xff0c;封装好出现无法分析或处理pass[specialize]应答文件 解决办法&#x…

Java基础:String类、static关键字、Arrays类、Math类

第一章 String类 1.1 String类概述 概述 java.lang.String类代表字符串。Java程序中所有的字符串文字&#xff08;例如"abc"&#xff09;都可以被看作是实现此类的实例。 类String中包括用于检查各个字符串的方法&#xff0c;比如用于比较字符串&#xff0c;搜索…

[附源码]计算机毕业设计JAVA新冠疫苗线上预约系统

[附源码]计算机毕业设计JAVA新冠疫苗线上预约系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM my…

Web3.0 DApp(去中心化应用程序)设计架构

先来回顾下 Web2.0 应用程序架构&#xff0c;一图胜千言&#xff1a; 图示是对大多数 Web 2.0 应用程序如何工作的一个很好的抽象总结。以一个博客平台为例&#xff1a; 首先&#xff0c;必须有一个地方来存储基本数据&#xff0c;也就是数据库&#xff1b; 其次&#xff0c;…

快速串联 RNN / LSTM / Attention / transformer / BERT / GPT(未完待续)

参考&#xff1a; 李宏毅2021/2022春机器学习课程王树森 RNN & Transformer 教程 文章目录0. 背景&#xff1a;序列数据及相关任务1. 早期序列模型1.1 循环神经网络 RNN1.2 长短期记忆网络 LSTM1.3 改善 RNN/LSTM 的三个技巧1.3.1 通过堆叠扩展为深度模型1.3.2 使用双向模…

使用学校的服务器跑深度学习

&#x1f31e;欢迎来到深度学习的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f320;本阶段属于练气阶段&#xff0c;希望各位仙友顺利完…

原型工具与代码实现的差距及改进设想

背景 最近经常根据墨刀设计的原型开发微信小程序页面&#xff0c;使用的多了总感觉原型只能参考&#xff0c;原型跟代码实现总差一小步。原型中提供的CSS并不能直接复制到代码中&#xff0c;导致编码人员复刻原型设计时总有差距。本文先列举出一些原型和实现的差距&#xff0c;…

RS232/RS485信号转12路模拟信号 隔离D/A转换器YL34

特点&#xff1a; ● RS-485/232接口&#xff0c;隔离转换成12路标准模拟信号输出 ● 可选型输出4-20mA或0-10V控制其他设备 ● 模拟信号输出精度优于 0.2% ● 可以程控校准模块输出精度 ● 信号输出 / 通讯接口之间隔离耐压3000VDC ● 宽电源供电范围&#xff1a;10 ~ 3…

信息收集的工具简介和常见操作

目录 前言 域名信息 工具 子域名信息 工具 域名DNS解析信息 工具 ip信息 工具 CDN绕过 方法 工具 端口服务信息 常见端口总结 基本概念 扫描工具 指纹识别 识别对象 cms指纹识别 识别思路 工具 其他工具 cdn识别 常用工具 waf识别 触发 工具 Google…

营业利润里首次突破两位数,瑞幸能否延续神话?

近期&#xff0c;瑞幸咖啡公布了Q3财报&#xff0c;三季度继续延续了上半年良好的增长势能。总净收入39亿元&#xff0c;同比增长65.7%&#xff0c;营业利润率首次突破双位数达到了15%。 门店数量增长&#xff1a;Q3新增651家&#xff0c;达到7846家门店。从开店节奏看&#…

RCNN算法思想简单讲解概述————(究极简单的讲述和理解)

学习的过程中发现一个问题&#xff0c;如果不能大概的了解一下一个算法的思想直接去看他的论文&#xff0c;或者去看他算法的讲解就很痛苦&#xff0c;看不懂&#xff0c;学的效率也非常低&#xff0c;类似我之前发的RCNN论文精度的博客。RCNN目标检测算法内容详解&#xff08;…

FreeIPA 统一身份认证实现

1、FreeIPA 简介 FreeIPA是一个用于Linux/Unix环境开源的身份管理系统,提供集中式帐户管理和身份验证,与Windows Active Directory或LDAP的作用类似。FreeIPA集成了389目录服务器、MIT Kerberos、Apache HTTP服务器、NTP、DNS、Dogtag(证书系统)和SSSD,使其成为标识管理、…

使用Flink的各种技术实现WordCount逻辑

使用Flink的各种技术实现WordCount逻辑 在大数据程序中&#xff0c;WordCount程序实现了统计词频的作用&#xff0c;这个WordCount程序也往往在大数据分析处理中一直占着非常重要的地位。统计一天内某网站的访问次数&#xff0c;需要对网站排序后求其词频&#xff0c;统计一段…

【智能电网随机调度】智能电网的双层模型时间尺度随机优化调度(Matlab代码实现)

目录 1 概述 2 数学模型 3 运行结果 4 结论 5 参考文献 6 Matlab代码实现 1 概述 随着可再生能源发电量的增加&#xff0c;配电网的能源管理正成为一项计算上具有挑战性的任务。来自光伏&#xff08;PV&#xff09;装置的太阳能可以在一分钟内发生显着变化。可以命令光伏…

MongoDB安装Mac M1

1、下载安装包&#xff1a; axInstall MongoDB Community Edition on macOS — MongoDB Manualhttps://www.mongodb.com/docs/v6.0/tutorial/install-mongodb-on-os-x/下载解压&#xff0c;重命名为mongodb 放到 /usr/local 目录下 2、配置文件打开配置文件 open -e .bash_p…

java计算机毕业设计ssm某大学校园竞赛管理系统07494(附源码、数据库)

java计算机毕业设计ssm某大学校园竞赛管理系统07494&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&…

阿里云企业用户12月活动:c7/g7/r7系列产品报价出炉

阿里云企业用户12月优惠活动开始了&#xff0c;阿里云超企业用户热销产品&#xff0c;c7/g7/r7系列&#xff0c;4折起&#xff0c;性能优异&#xff0c;助您数智化转型。通过芯片快速路径加速&#xff0c;实现存储、网络性能以及计算稳定性的大幅提升。下方卡片显示价格为企业新…

一文读懂 HTTP/2 特性

HTTP/2 是 HTTP 协议自 1999 年 HTTP 1.1 发布后的首个更新&#xff0c;主要基于 SPDY 协议。由互联网工程任务组&#xff08;IETF&#xff09;的 Hypertext Transfer Protocol Bis&#xff08;httpbis&#xff09;工作小组进行开发。该组织于2014年12月将HTTP/2标准提议递交至…