RateLimiter实现原理
RateLimiter实现原理
简介
代码整理
- resync()
- 惰性更新 落后的nextFreeTicketMicros 为当前时间, 也就是如果当前时间已经大于nextFreeTicketMicros,则更新 nextFreeTicketMicros 为当前时间
- 惰性更新 storedPermits 加上(当前时间-落后的nextFreeTicketMicros)过程中可以生成多少令牌,作为存储的令牌,不会超过 maxPermits 最大令牌数
- reserveAndGetWaitLength()
- 计算 nextFreeTicketMicros - 当前时间,代表这个请求需要等待的时间: microsToNextFreeTicket
- 根据当前令牌桶里的数目,计算还需要多少时间才能偿还 被借用的令牌数目 ,并叠加到下次请求的放行时间:nextFreeTicketMicros
- 返回 microsToNextFreeTicket
- 睡眠 waitMicros 这段时间 Thread.sleep()
- doSetRate() 已经在同步代码中
- 执行resync(),惰性更新 nextFreeTicketMicros 和 storedPermits。
- maxPermits = maxBurstSeconds * permitsPerSecond; 即 最大许可为1秒钟生成多少个许可。作为最大令牌。
- 当每秒生成令牌小于1时,相当于系统不会存储令牌。
结合源码
demo
public void contextLoad5() throws InterruptedException {
// 每秒0.1个请求, 阻塞直到获取到令牌
RateLimiter rateLimiter = RateLimiter.create(0.1);
CountDownLatch countDownLatch = new CountDownLatch(10);
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
int times = 0;
while (true) {
boolean b = rateLimiter.tryAcquire();
if (b) {
times++;
System.out.println(Thread.currentThread().getName() + "当前请求i:【" + times + "】");
}
else {
System.out.println(Thread.currentThread().getName() + "表示已达到限流阈值,拒绝请求..." + times);
}
try {
Thread.sleep(10 * 1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
countDownLatch.await();
}
RateLimiter.create(0.1);
// 当前令牌数目
double storedPermits;
// 最大令牌数目
double maxPermits;
// 每个请求需要多少
volatile double stableIntervalMicros;
// 锁 : acquire / tryAcquire / setRate 的锁
private final Object mutex;
// 下一个请求可以放行的时间
private long nextFreeTicketMicros;
// permitsPerSecond: 每秒钟生成多少个请求
static RateLimiter create(RateLimiter.SleepingTicker ticker, double permitsPerSecond) {
RateLimiter rateLimiter = new RateLimiter.Bursty(ticker, 1.0D);
rateLimiter.setRate(permitsPerSecond);
return rateLimiter;
}
public final void setRate(double permitsPerSecond) {
Preconditions.checkArgument(permitsPerSecond > 0.0D && !Double.isNaN(permitsPerSecond), "rate must be positive");
// 使用 mutex 作为锁
synchronized(this.mutex) {
// 落后的nextFreeTicketMicros ,意味着 nowMicros > this.nextFreeTicketMicros
// 惰性更新 落后的nextFreeTicketMicros 为当前时间, 也就是如果当前时间已经大于nextFreeTicketMicros,则更新 nextFreeTicketMicros 为当前时间
// 惰性更新 storedPermits 加上(当前时间-落后的nextFreeTicketMicros)过程中可以生成多少令牌,作为存储的令牌,不会超过 maxPermits 最大令牌数
this.resync(this.readSafeMicros());
// 生成一个令牌需要的时间(微秒): stableIntervalMicros = 1 * 1000 * 1000 / 每秒钟生成多少个请求
double stableIntervalMicros = (double)TimeUnit.SECONDS.toMicros(1L) / permitsPerSecond;
this.stableIntervalMicros = stableIntervalMicros;
// 生成最大令牌数目: maxPermits = 1L * 每秒钟生成多少个请求
this.doSetRate(permitsPerSecond, stableIntervalMicros);
}
}
private void resync(long nowMicros) {
if (nowMicros > this.nextFreeTicketMicros) {
this.storedPermits = Math.min(this.maxPermits, this.storedPermits + (double)(nowMicros - this.nextFreeTicketMicros) / this.stableIntervalMicros);
this.nextFreeTicketMicros = nowMicros;
}
}
void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
double oldMaxPermits = this.maxPermits;
this.maxPermits = this.maxBurstSeconds * permitsPerSecond;
this.storedPermits = oldMaxPermits == 0.0D ? 0.0D : this.storedPermits * this.maxPermits / oldMaxPermits;
}
boolean b = rateLimiter.tryAcquire();
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) {
long timeoutMicros = unit.toMicros(timeout);
// 校验一下permits是不是负数
checkPermits(permits);
long microsToWait;
// 使用 mutex 作为锁
synchronized(this.mutex) {
long nowMicros = this.readSafeMicros();
// 如果 下一个请求可以放行的时间 大于 当前时间+允许的偏移时间, 说明还没到时间,不能放行
if (this.nextFreeTicketMicros > nowMicros + timeoutMicros) {
return false;
}
// 否则就是已经可以放行了
// 计算 nextFreeTicketMicros - 当前时间,代表这个请求需要等待的时间,作为结果返回microsToWait
// 同时叠加下一个请求需要的时间 加上 偿还本次借用的令牌需要的时间
microsToWait = this.reserveNextTicket((double)permits, nowMicros);
}
// 进行等待 microsToWait, Thread.sleep 一段时间
this.ticker.sleepMicrosUninterruptibly(microsToWait);
// 获取令牌成功
return true;
}
// requiredPermits : 这次请求总共需要多少个令牌
private long reserveNextTicket(double requiredPermits, long nowMicros) {
// 落后的nextFreeTicketMicros ,意味着 nowMicros > this.nextFreeTicketMicros
// 惰性更新 落后的nextFreeTicketMicros 为当前时间
// 惰性更新 storedPermits 为 (当前时间-落后的nextFreeTicketMicros)过程中产生的 令牌数目
this.resync(nowMicros);
// microsToNextFreeTicket (需要等待的时间) = 下一个请求可以放行的时间 - 当前时间
long microsToNextFreeTicket = this.nextFreeTicketMicros - nowMicros;
// storedPermitsToSpend (对于这次请求,当下可以立刻提供多少个令牌) = Math.min(requiredPermits, this.storedPermits);
double storedPermitsToSpend = Math.min(requiredPermits, this.storedPermits);
// freshPermits (对于这次请求,还需要提供多少个令牌) = requiredPermits - storedPermitsToSpend;
double freshPermits = requiredPermits - storedPermitsToSpend;
// 需要等待的时间 = 从 storedPermits 中借出 storedPermitsToSpend 需要的时间(默认为0) + 还需要多少个令牌 * 生成一个令牌需要的时间
long waitMicros = this.storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend) + (long)(freshPermits * this.stableIntervalMicros);
// 更新 下一个请求可以放行的时间 加上 重新生成借用的令牌需要的时间
this.nextFreeTicketMicros += waitMicros;
// 令牌消耗
this.storedPermits -= storedPermitsToSpend;
return microsToNextFreeTicket;
}
long storedPermitsToWaitTime(double storedPermits, double permitsToTake) {
return 0L;
}
rateLimiter.acquire();
public double acquire(int permits) {
// 校验一下permits是不是负数
checkPermits(permits);
long microsToWait;
// 使用 mutex 作为锁
synchronized (mutex) {
// 获取这个请求需要等待多长时间,更新下次请求需要等待的时间
microsToWait = reserveNextTicket(permits, readSafeMicros());
}
// 进行等待 microsToWait, Thread.sleep 一段时间
ticker.sleepMicrosUninterruptibly(microsToWait);
return 1.0 * microsToWait / TimeUnit.SECONDS.toMicros(1L);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类