Guava RateLimiter源码理解
SmoothBursty#
create()创建限流器#
重要参数#
- nextFreeTicketMicros 预计下次发放令牌的时间
- stableIntervalMicros 两次发放令牌之间的时间间隔
- maxPermits 最大能存储的令牌的数量
- storedPermits 已经存储的令牌数
public static RateLimiter create(double permitsPerSecond) {
/*
* The default RateLimiter configuration can save the unused permits of up to one second. This
* is to avoid unnecessary stalls in situations like this: A RateLimiter of 1qps, and 4 threads,
* all calling acquire() at these moments:
*
* T0 at 0 seconds
* T1 at 1.05 seconds
* T2 at 2 seconds
* T3 at 3 seconds
*
* Due to the slight delay of T1, T2 would have to sleep till 2.05 seconds, and T3 would also
* have to sleep till 3.05 seconds.
*/
return create(permitsPerSecond, SleepingStopwatch.createFromSystemTimer());
}
static RateLimiter create(double permitsPerSecond, SleepingStopwatch stopwatch) {
RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);
rateLimiter.setRate(permitsPerSecond);
return rateLimiter;
}
public final void setRate(double permitsPerSecond) {
checkArgument(
permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive");
//同步设置
synchronized (mutex()) {
doSetRate(permitsPerSecond, stopwatch.readMicros());
}
}
SleepingStopwatch.createFromSystemTimer()
此处创建一个秒表StopWatch:
-
readMicros() 读取从秒表开始创建起到当前的纳秒时间
-
sleepMicrosUninterruptibly() 阻塞等待, 等待生产出本次requiredPermits数量所需要的时长, 如果之前还有等待的那就等待更长时间
public static final SleepingStopwatch createFromSystemTimer() {
return new SleepingStopwatch() {
//初始化秒表的状态
final Stopwatch stopwatch = Stopwatch.createStarted();
//读取从秒表开始创建起到当前流逝的时间, 相对值
@Override
protected long readMicros() {
return stopwatch.elapsed(MICROSECONDS);
}
//阻塞等待
@Override
protected void sleepMicrosUninterruptibly(long micros) {
if (micros > 0) {
Uninterruptibles.sleepUninterruptibly(micros, MICROSECONDS);
}
}
};
}
createStarted()初始化秒表的状态
public static Stopwatch createStarted() {
return new Stopwatch().start();
}
public Stopwatch start() {
checkState(!isRunning, "This stopwatch is already running.");
//秒表
isRunning = true;
startTick = ticker.read();
return this;
}
doSetRate()初始化的核心流程
final void doSetRate(double permitsPerSecond, long nowMicros) {
//同步
resync(nowMicros);
//每个令牌生成的时间间隔
double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;
this.stableIntervalMicros = stableIntervalMicros;
//
doSetRate(permitsPerSecond, stableIntervalMicros);
}
- 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;
}
}
- doSetRate()时间间隔
@Override
void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
double oldMaxPermits = this.maxPermits;
maxPermits = maxBurstSeconds * permitsPerSecond;
if (oldMaxPermits == Double.POSITIVE_INFINITY) {
storedPermits = maxPermits;
} else {
storedPermits =
(oldMaxPermits == 0.0)
? 0.0 // initial state
//当动态设置QPS即permitsPerSecond, 则按比例设置storedPermits
: storedPermits * maxPermits / oldMaxPermits;
}
}
acquire() 阻塞请求#
@CanIgnoreReturnValue
public double acquire(int permits) {
//获取令牌
long microsToWait = reserve(permits);
//阻塞等待
stopwatch.sleepMicrosUninterruptibly(microsToWait);
return 1.0 * microsToWait / SECONDS.toMicros(1L);
}
final long reserve(int permits) {
checkPermits(permits);
//同步
synchronized (mutex()) {
return reserveAndGetWaitLength(permits, stopwatch.readMicros());
}
}
final long reserveAndGetWaitLength(int permits, long nowMicros) {
long momentAvailable = reserveEarliestAvailable(permits, nowMicros);
//momentAvailable就是resync之后的nextFreeTicketMicros,如果nowMicros > nextFreeTicketMicros, 最后nowMicros == nextFreeTicketMicros, 那么就是不需要sleep的, 因为此时已经认为等了足够的时间,已经认为产生了足够的ticket可以使用, 所以可以放行。
return max(momentAvailable - nowMicros, 0);
}
reserveEarliestAvailable()预订最快可用的令牌
@Override
final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
//同上,同步
resync(nowMicros);
long returnValue = nextFreeTicketMicros;
//当前请求令牌数requiredPermits
double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
//冗余请求令牌数freshPermits, 是小数
double freshPermits = requiredPermits - storedPermitsToSpend;
//storedPermitsToWaitTime对于SmoothBursty为0,非预热型
long waitMicros = storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
//冗余请求令牌数需等待的总时间,如果没有冗余请求此处nextFreeTicketMicros也不会变化, 补充足以本次消费的令牌还需要等待的时间
+ (long) (freshPermits * stableIntervalMicros);
//重置令牌生成的起始时间为当前时间
this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
this.storedPermits -= storedPermitsToSpend;
return returnValue;
}
tryAcquire() 非阻塞请求#
//timeout期望等待的时间是否可以取到令牌
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) {
//
long timeoutMicros = max(unit.toMicros(timeout), 0);
checkPermits(permits);
long microsToWait;
synchronized (mutex()) {
long nowMicros = stopwatch.readMicros();
//判断是否能取到令牌
if (!canAcquire(nowMicros, timeoutMicros)) {
return false;
} else {
//走acquire()流程
microsToWait = reserveAndGetWaitLength(permits, nowMicros);
}
}
stopwatch.sleepMicrosUninterruptibly(microsToWait);
return true;
}
private boolean canAcquire(long nowMicros, long timeoutMicros) {
return queryEarliestAvailable(nowMicros) - timeoutMicros <= nowMicros;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程