限流系列:guava rateLimiter

目录

令牌桶算法

Guava-RateLimiter

数据模型

示例

大致流程

acquire

reserve

​​​​​​​reserveAndGetWaitLength

​​​​​​​reserveEarliestAvailable

​​​​​​​resync

​​​​​​​doSetRate


令牌桶 算法

令牌桶算法就是以固定速率生成令牌放入桶中,每个请求都需要从桶中获取令牌,没有获取到令牌的请求会被阻塞限流(桶中的令牌不够的时候);只要能从桶里取出令牌就能通过。

当令牌消耗速度小于生成的速度时,令牌桶内就会预存这些未消耗的令牌(直到桶的上限);当有突发流量进来时,可以直接从桶中取出令牌,而不会被限流,从而支持突发流量的快速处理。

Guava - RateLimiter

不管是令牌桶算法还是漏桶算法都可以用延迟计算的方式来实现,延迟计算指的是不需要单独的线程来定时生成令牌或者从漏桶中定时取出请求,而是由调用限流器的线程自己去计算是否有足够的令牌以及需要sleep的时间,延迟计算的方式可以节省一个线程资源。

Guava 的 RateLimiter 是一个用于控制访问速率的工具类,常用于限流(如每秒处理请求的数量)。它基于令牌桶算法实现。

​​​​​​​数据模型

|------------------|--------------|---------------------------------------------------------|
| 英文名称 | 中文名称 | 备注 |
| permitsPerSecond | 每秒允许许可数量 | |
| | 桶容量 | maxBurstSeconds * permitsPerSecond默认 maxBurstSeconds=1 |

示例

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| // 创建一个每秒允许 10 个许可的 RateLimiter RateLimiter rateLimiter = RateLimiter.create(10.0); for (int i = 0; i < 10; i++) { // 获取一个许可,如果没有可用许可,则会阻塞直到有许可 rateLimiter.acquire(); // 执行受控操作 log.info("Processing request " + i); } |

大致流程

点击示例里的acquire()方法,如下所示:

​​​​​​​acquire

@CanIgnoreReturnValue

public double acquire() {

return acquire(1);

}

@CanIgnoreReturnValue

public double acquire(int permits) {

long microsToWait = reserve(permits);

stopwatch.sleepMicrosUninterruptibly(microsToWait);

return 1.0 * microsToWait / SECONDS.toMicros(1L);

}

点击reserve()方法,如下所示:

​​​​​​​reserve

final long reserve(int permits) {

checkPermits(permits);

synchronized (mutex()) {

return reserveAndGetWaitLength(permits, stopwatch.readMicros());

}

}

在这里,计算当前时间的微秒数,作为参数调用reserveAndGetWaitLength方法。

如下所示:

​​​​​​​reserveAndGetWaitLength

final long reserveAndGetWaitLength(int permits, long nowMicros) {

long momentAvailable = reserveEarliestAvailable(permits, nowMicros);

return max(momentAvailable - nowMicros, 0);

}

点击reserveEarliestAvailable()方法,如下所示:

​​​​​​​reserveEarliestAvailable

@Override

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;

}

点击resync()方法,如下所示:

​​​​​​​resync

void resync(long nowMicros) {

// if nextFreeTicket is in the past, resync to now

if (nowMicros > nextFreeTicketMicros) {

double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();

storedPermits = min(maxPermits, storedPermits + newPermits);

nextFreeTicketMicros = nowMicros;

}

}

在这里:

  1. 判断当前时刻是否大小上次令牌更新时间,是则进入下一步(这是与resilience4j-ratelimiter差别最大的地方)
  2. 计算上次令牌更新时间到当前时刻的时间差(微秒)
  3. 获取生成单个令牌所需的时间间隔(微秒/令牌)
  4. 计算上次令牌更新时间到当前时刻的时间差这段时间内允许生成的令牌数量,即步骤1除以步骤2。

点击coolDownIntervalMicros()方法,如下所示:

​​​​​​​coolDownIntervalMicros

double coolDownIntervalMicros() {

return stableIntervalMicros;

}

找到stableIntervalMicros变更赋值的地方,如下所示:

​​​​​​​doSetRate

final void doSetRate(double permitsPerSecond, long nowMicros) {

resync(nowMicros);

double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;

this.stableIntervalMicros = stableIntervalMicros;

doSetRate(permitsPerSecond, stableIntervalMicros);

}

在这里,将SECONDS.toMicros(1L) / permitsPerSecond得到生成单个令牌所需的时间。

相关推荐
zzywxc78727 分钟前
AI赋能千行百业:金融、医疗、教育、制造业的落地实践与未来展望
java·人工智能·python·microsoft·金融·golang·prompt
一只学java的小汉堡34 分钟前
Spring Boot 配置详解:从引导器到注解实战(初学者指南)
java·spring boot·后端
独自破碎E41 分钟前
归并排序的递归和非递归实现
java·算法·排序算法
一叶飘零_sweeeet1 小时前
线程同步实战指南:从 bug 根源到锁优化的终极之路
java·线程·线程同步
失散131 小时前
分布式专题——25 深入理解网络通信和TCP、IP协议
java·分布式·网络协议·tcp/ip·架构
zz0723203 小时前
Java 集合体系 —— List 篇
java·list·集合体系
-雷阵雨-3 小时前
数据结构——LinkedList和链表
java·开发语言·数据结构·链表·intellij-idea
fly-phantomWing7 小时前
Maven的安装与配置的详细步骤
java·后端·maven·intellij-idea
2401_8414956410 小时前
【数据结构】红黑树的基本操作
java·数据结构·c++·python·算法·红黑树·二叉搜索树
学编程的小鬼10 小时前
SpringBoot 自动装配原理剖析
java·spring boot·后端