一、问题描述  
  某天A君突然发现自己的接口请求量突然涨到之前的10倍,没多久该接口几乎不可使用,并引发连锁反应导致整个系统崩溃。如何应对这种情况呢?生活给了我们答案:比如老式电闸都安装了保险丝,一旦有人使用超大功率的设备,保险丝就会烧断以保护各个电器不被强电流给烧坏。同理我们的接口也需要安装上“保险丝”,以防止非预期的请求对系统压力过大而引起的系统瘫痪,当流量过大时,可以采取拒绝或者引流等机制。

二、常用的限流算法
      常用的限流算法有两种:漏桶算法和令牌桶算法,这篇博文介绍得比较清晰(过载保护算法浅析)。

漏桶算法思路很简单,请求先进入到漏桶里,漏桶以一定的速度出水,当水请求过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。

对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。如图2所示,令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则阻塞等待令牌空闲再执行。

三、限流工具类RateLimiter
   google开源工具包guava提供了限流工具类RateLimiter,该类基于“令牌桶算法”,非常方便使用。该类的接口描述请参考:RateLimiter接口描述,具体的使用请参考:RateLimiter使用实践。

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

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

应用时定成这样:

private static RateLimiter limiter = RateLimiter.create(5);

RateLimiter 使用 Dem2:

import com.google.common.util.concurrent.RateLimiter;
public class Test {public static void main(String[] args) {testNoRateLimiter();testWithRateLimiter();}public static void testNoRateLimiter() {Long start = System.currentTimeMillis();for (int i = 0; i < 100; i++) {System.out.println("call execute.." + i); // 未限流总耗时:3}Long end = System.currentTimeMillis();System.out.println("未限流总耗时:" + (end - start)); // 进行限流后耗时:9912}public static void testWithRateLimiter() {Long start = System.currentTimeMillis();// 每秒不超过10个任务被提交RateLimiter limiter = RateLimiter.create(10.0);for (int i = 0; i < 100; i++) {// 请求RateLimiter, 超过permits会被阻塞,然后等待获取令牌limiter.acquire();System.out.println("call execute.." + i);}Long end = System.currentTimeMillis();System.out.println("进行限流后耗时:" + (end - start));}
}

RateLimiter类似于JDK的信号量Semphore,他用来限制对资源并发访问的线程数

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

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

通过设置许可证的速率来定义RateLimiter。在默认配置下,许可证会在固定的速率下被分配,速率单位是每秒多少个许可证。为了确保维护配置的速率,许可会被平稳地分配,许可之间的延迟会做调整。
可能存在配置一个拥有预热期的RateLimiter 的情况,在这段时间内,每秒分配的许可数会稳定地增长直到达到稳定的速率。

方法细节

create
public static RateLimiter create(double permitsPerSecond)
根据指定的稳定吞吐率创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少查询)。
The returned RateLimiter ensures that on average no more than permitsPerSecond are issued during any given second, with sustained requests being smoothly spread over each second. When the incoming request rate exceeds permitsPerSecond the rate limiter will release one permit every (1.0 / permitsPerSecond) seconds. When the rate limiter is unused, bursts of up to permitsPerSecond permits will be allowed, with subsequent requests being smoothly limited at the stable rate of permitsPerSecond.
返回的RateLimiter 确保了在平均情况下,每秒发布的许可数不会超过permitsPerSecond,每秒钟会持续发送请求。当传入请求速率超过permitsPerSecond,速率限制器会每秒释放一个许可(1.0 / permitsPerSecond 这里是指设定了permitsPerSecond为1.0) 。当速率限制器闲置时,允许许可数暴增到permitsPerSecond,随后的请求会被平滑地限制在稳定速率permitsPerSecond中。
参数:
permitsPerSecond – 返回的RateLimiter的速率,意味着每秒有多少个许可变成有效。
抛出:
IllegalArgumentException – 如果permitsPerSecond为负数或者为0
create
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
setRate
public final void setRate(double permitsPerSecond)
更新RateLimite的稳定速率,参数permitsPerSecond 由构造RateLimiter的工厂方法提供。调用该方法后,当前限制线程不会被唤醒,因此他们不会注意到最新的速率;只有接下来的请求才会。需要注意的是,由于每次请求偿还了(通过等待,如果需要的话)上一次请求的开销,这意味着紧紧跟着的下一个请求不会被最新的速率影响到,在调用了setRate 之后;它会偿还上一次请求的开销,这个开销依赖于之前的速率。RateLimiter的行为在任何方式下都不会被改变,比如如果 RateLimiter 有20秒的预热期配置,在此方法被调用后它还是会进行20秒的预热。
参数:
permitsPerSecond – RateLimiter的新的稳定速率
抛出:
IllegalArgumentException – 如果permitsPerSecond为负数或者为0
getRate
public final double getRate()
返回RateLimiter 配置中的稳定速率,该速率单位是每秒多少许可数。它的初始值相当于构造这个RateLimiter的工厂方法中的参数permitsPerSecond ,并且只有在调用setRate(double)后才会被更新。
acquire
public double acquire()
从RateLimiter获取一个许可,该方法会被阻塞直到获取到请求。如果存在等待的情况的话,告诉调用者获取到该请求所需要的睡眠时间。该方法等同于acquire(1)。
返回:
time spent sleeping to enforce rate, in seconds; 0.0 if not rate-limited
执行速率的所需要的睡眠时间,单位为妙;如果没有则返回0
Since:
16.0 (版本13.0没有返回值)

有一点很重要,那就是请求的许可数从来不会影响到请求本身的限制(调用acquire(1) 和调用acquire(1000) 将得到相同的限制效果,如果存在这样的调用的话),但会影响下一次请求的限制,也就是说,如果一个高开销的任务抵达一个空闲的RateLimiter,它会被马上许可,但是下一个请求会经历额外的限制,从而来偿付高开销任务。注意:RateLimiter 并不提供公平性的保证。

所以下面的public double acquire(int permits),应该会很少使用到吧..

acquire
public double acquire(int permits)
从RateLimiter获取指定许可数,该方法会被阻塞直到获取到请求数。如果存在等待的情况的话,告诉调用者获取到这些请求数所需要的睡眠时间。
参数:
permits – 需要获取的许可数
返回:
执行速率的所需要的睡眠时间,单位为妙;如果没有则返回0
抛出:
IllegalArgumentException – 如果请求的许可数为负数或者为0
Since:
16.0 (版本13.0没有返回值)
tryAcquire
public boolean tryAcquire(long timeout,TimeUnit unit)
从RateLimiter获取许可如果该许可可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待)。该方法等同于tryAcquire(1, timeout, unit)。
参数:

  • timeout – 等待许可的最大时间,负数以0处理
  • unit – 参数timeout 的时间单位

返回:
true表示获取到许可,反之则是false
抛出:
IllegalArgumentException – 如果请求的许可数为负数或者为0

tryAcquire
public boolean tryAcquire(int permits)
从RateLimiter 获取许可数,如果该许可数可以在无延迟下的情况下立即获取得到的话。该方法等同于tryAcquire(permits, 0, anyUnit)。
参数:
permits – 需要获取的许可数
返回:
true表示获取到许可,反之则是false
抛出:
IllegalArgumentException – 如果请求的许可数为负数或者为0
Since:
14.0
tryAcquire
public boolean tryAcquire()
从RateLimiter 获取许可,如果该许可可以在无延迟下的情况下立即获取得到的话。
该方法等同于tryAcquire(1)。
返回:
true表示获取到许可,反之则是false
Since:
14.0
tryAcquire
public boolean tryAcquire(int permits,long timeout,TimeUnit unit)
从RateLimiter 获取指定许可数如果该许可数可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可数的话,那么立即返回false (无需等待)。
参数:
permits – 需要获取的许可数
timeout – 等待许可数的最大时间,负数以0处理
unit – 参数timeout 的时间单位
返回:
true表示获取到许可,反之则是false
抛出:
IllegalArgumentException -如果请求的许可数为负数或者为0

参考来源于:

https://blog.csdn.net/JIESA/article/details/50412027

https://www.cnblogs.com/exceptioneye/p/4824394.html

使用Guava的RateLimiter做限流相关推荐

  1. 【Guava】使用Guava的RateLimiter做限流

    2019独角兽企业重金招聘Python工程师标准>>> 一.常见的限流算法 目前常用的限流算法有两个:漏桶算法和令牌桶算法. 1.漏桶算法 漏桶算法的原理比较简单,请求进入到漏桶中, ...

  2. RateLimiter google限流组件试析(SmoothBursty/SmoothWarmingUp)

    RateLimiter google限流组件试析(SmoothBursty/SmoothWarmingUp) RateLimiter是guava包里的限流组件,位于com.google.common. ...

  3. Rabbitmq专题:rabbitmq消费端如何做限流?

    文章目录 1. 什么是消费端的限流? 2. 解决方案 3. 代码示例 1. 什么是消费端的限流? 场景:在订单高峰期,rabbitmq上已经堆积了很多消息等待消费,如果没有任何限流措施,贸然启动一个消 ...

  4. 系统不做限流,我看你是对中国人口数量有什么误解

    目录 限流 怎么做限流? 获得系统能力上限.处理被限制流量 具体如何限流? 固定窗口 滑动窗口 漏桶 令牌桶 做限流的最佳实践 分布式系统中带来的新挑战 纵 在软件架构领域,"限流" ...

  5. java 使用Guava的RateLimiter做接口限流+redis的lua脚本做IP防刷

    需求: 每个IP在指定的时间内可以请求某一个接口多少次,如果请求次数超过指定数,就返回拒绝信息 没做IP防刷之前,请求多了之后服务蹦了 做防刷之后 当然,还有限流 直接上代码  接口注解代码: imp ...

  6. 面试官 | 讲一下如何给高并发系统做限流?

    作者 | nick hao 来源 | uee.me/cDuRD 在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流.本文结合作者的一些经验介绍限流的相关概念.算法和常规的实现方式. 缓存 缓存 ...

  7. 单个接口添加超时时间_SpringBoot使用Guava令牌桶对接口限流

    pom.xml引入jar包 com.google.guava guava 29.0-jre 代码 @RestControllerpublic class HelloController { //创建令 ...

  8. RateLimiter实现限流

    简介 令牌桶算法则是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌.桶中存放的令牌数有最大上限,超出之后就被丢弃或者拒绝.当流量或者网络请求到达时,每个请求都要获取一个令牌,如果能够获取到,则直 ...

  9. 程序员修神之路--高并发优雅的做限流(有福利)

    点击上方蓝色字体,关注我们 菜菜哥,有时间吗? YY妹,什么事? 我最近的任务是做个小的秒杀活动,我怕把后端接口压垮,X总说这可关系到公司的存亡 简单呀,你就做个限流呗 这个没做过呀,菜菜哥,帮妹子写 ...

最新文章

  1. 开发小程序商城必看:社交电商的销售玩法
  2. 地大计算机学院教授,张静(地大教授)
  3. oracle启动crs要多久,ORACLE RAC crs 无法启动
  4. 带防夹功能的升降器原理_桌面光污染必不可少——骨伽Bunker RGB鼠标线夹
  5. 山东自考c语言程序设计停考了吗,2018山东自考停考专业有哪些
  6. 超过12000个零件,钢琴就是一部复杂的机器
  7. 如何更新Postgresql的Jsonb数组
  8. linux HZ 值_Linux的serial串口控制台
  9. Python实现双端队列
  10. MonoRail学习笔记十八:在VM中可以使用哪些系统变量
  11. 在CentOS 7中使用Sendmail通过PHP发送邮件
  12. Tensor Flow量化里representative_dataset参数是什么意思?
  13. 2021年软考VRRP虚拟路由冗余技术
  14. 通过YAJL生成json语句
  15. 以前的模板太糟糕了?
  16. 山东大学高频电子线路实验四 振幅调制与解调实验详解
  17. 芝麻信用分有哪些计算维度?关于大数据风控的87个问题
  18. 神奇英语语法系列(二)——名词性从句
  19. Microsoft ADO Data Control,version 6.0(OLEDB) 不支持此接口
  20. 小人有三种,这种最阴险,最好策略不是硬杠

热门文章

  1. 固定资产减值准备、累计折旧
  2. 个人电脑厂商艰难涉水家庭娱乐市场
  3. Go 反射机制详解及实例 【Go语言圣经笔记】
  4. CocosCreator之Spine系列(一):spine动画回调
  5. 主机与虚拟机之间无法粘贴复制
  6. 人工智能实战2019第六次作业 焦宇恒
  7. MYSQL inserOrUpdate三种写法
  8. 【鸿蒙】鸿蒙App应用-《记账软件》登录,注册,找回密码功能
  9. 1月第1周榜单丨B站UP主排行榜(飞瓜数据B站)发布!
  10. python中多行语句可以用反斜杠来实现_Python中的多行语句可以使用反斜杠来实现...