限流算法及guava限流器RateLimiter实现
·
限流是通过对并发请求进行限速,继而达到保护高并发系统的目的。常见限流算法有漏桶算法及令牌桶算法。其流程可参考下图。

漏桶算法思路比较简单,就是将请求放入桶中,然后以固定速率处理桶中请求,当桶满后会丢弃新来的请求。漏桶算法一般依靠有界队列实现。
令牌桶算法则是先创建一个固定大小的令牌桶,按照固定速率向桶中加入令牌,每个请求要先获取到令牌才能进行处理。 没获取到令牌的请求则放入缓冲区等待或直接丢弃。
guava限流器RateLimiter是通过令牌桶实现的经典限流器。其使用也比较简单,使用代码如下。
首先引入guava jar
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>24.1-jre</version>
</dependency>
使用限流器
// 创建每秒处理两个请求的令牌桶
static RateLimiter limiter = RateLimiter.create(2);
public static void main(String[] args) {
for (int i = 0; i < 8; i++) {
new Thread("thread-" + i) {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + new Date());
// 限流器限流
limiter.acquire();
System.out.println("限流后执行时间" + new Date());
}
}.start();
}
}
RateLimiter实现
create方法创建
@VisibleForTesting
static RateLimiter create(double permitsPerSecond, SleepingStopwatch stopwatch) {
// 创建实例,设置保存令牌的最大时间,默认1秒
RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);
// 设置令牌产生速率
rateLimiter.setRate(permitsPerSecond);
return rateLimiter;
}
进入setRate
@Override
final void doSetRate(double permitsPerSecond, long nowMicros) {
//这里先通过调用resync生成令牌以及更新下一期令牌生成时间,调用时也会用到该方法,使用延迟调用的方式生成令牌,避免定时任务生成令牌。
resync(nowMicros);
double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;
this.stableIntervalMicros = stableIntervalMicros;
doSetRate(permitsPerSecond, stableIntervalMicros);
}
acquire申请令牌
@CanIgnoreReturnValue
public double acquire(int permits) {
// 得到可以获取令牌的时间
long microsToWait = reserve(permits);
// sllep等待
stopwatch.sleepMicrosUninterruptibly(microsToWait);
return 1.0 * microsToWait / SECONDS.toMicros(1L);
}
进入reserve
final long reserve(int permits) {
checkPermits(permits);
// 加锁进行令牌生成,最终调用SmoothRateLimiter.reserveEarliestAvailable方法
synchronized (mutex()) {
return reserveAndGetWaitLength(permits, stopwatch.readMicros());
}
}
@Override
final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
// 调用resync方法生成令牌
resync(nowMicros);
long returnValue = nextFreeTicketMicros;
// 计算实际能提供的令牌数storedPermitsToSpend
double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
// 计算需要新产生的令牌数freshPermits
double freshPermits = requiredPermits - storedPermitsToSpend;
// 根据freshPermits计算新产生这批令牌需要多长时间
long waitMicros =
storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
+ (long) (freshPermits * stableIntervalMicros);
// 更新下一次令牌可用时间戳与当前桶中令牌数量
this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
this.storedPermits -= storedPermitsToSpend;
return returnValue;
}
更多推荐



所有评论(0)