-
RateLimiter 的底层实现是啥?
作者:温安适
来源:https://my.oschina.net/floor/blog/4965200
前言
本文不是一个RateLimiter的详细分析,仅仅是概要分析。
令牌桶算法
一说到RateLimiter,必然要是说的令牌桶,它的大致逻辑如下
按图实现
令牌桶的图,网上到处可见,按图实现也非常简单,无非是定时添加令牌桶,并提供一个获取令牌的函数,博主实现了一遍代码如下:
import java.util.concurrent.*;
public class MyRateLimiter {
//令牌桶
BlockingQueue<Integer>TOKEN_BUCKET=new LinkedBlockingDeque<>(5);
public static void main(String[] args) {
MyRateLimiter myRateLimiter=new MyRateLimiter();
myRateLimiter.addTokenFixedRate();
for(int i=0;i<10;i++){
myRateLimiter.acqurie();
System.out.println("第几次执行i:" + i + ",执行时间为:" + System.currentTimeMillis());
}
}
/**
* 定时添加令牌
*/
public void addTokenFixedRate(){
ScheduledExecutorService scheduledExecutorService= Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(()->{
boolean suc=TOKEN_BUCKET.offer(1);
if(!suc){
System.out.println("令牌桶满了丢弃");
}
},0,200,TimeUnit.MILLISECONDS);
}
public void acqurie(){
while (TOKEN_BUCKET.poll()==null){};
}
}
测试结果如下,基本满足要求
RateLimiter概要实现
我一开始是按照自己实现的逻辑,去查看Guava的RateLimiter的源码的,结果发现RateLimiter根本没有集合充当桶,核心是记录了下一令牌产生的时间与现存令牌数,并动态更新它们。
概要逻辑图如下:
按照这个图看核心代码就比较容易了,摘录核心代码如下:
@CanIgnoreReturnValue
public double acquire(int permits) {
long microsToWait = reserve(permits);
stopwatch.sleepMicrosUninterruptibly(microsToWait);
return 1.0 * microsToWait / SECONDS.toMicros(1L);
}
//Reserve 一路向下能查到如下代码 reserveEarliestAvailable
final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
resync(nowMicros);
long returnValue = nextFreeTicketMicros;
// 现存令牌可以提供的令牌数
double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
//需要刷新的令牌数
double freshPermits = requiredPermits - storedPermitsToSpend;
//等待时间=需要刷新的令牌数*固定间隔+存储许可等待时间
long waitMicros =
storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
+ (long) (freshPermits * stableIntervalMicros);
//下次令牌生产时间=本次令牌生产时间+等待时间
this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
this.storedPermits -= storedPermitsToSpend;
return returnValue;
}
总结:RateLimiter根本没有集合充当桶,核心是记录了下一令牌产生的时间与现存令牌数,并动态更新它们。
最新更新
python爬虫及其可视化
使用python爬取豆瓣电影短评评论内容
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比