本文共 5343 字,大约阅读时间需要 17 分钟。
Guava库中的RateLimiter是一个强大的限流器,采用令牌桶算法来控制消息消费速度,防止下游服务因为消息过载而崩溃。RateLimiter的核心实现分为两类:基础限流器SmoothBursty和带有预热功能的SmoothWarmingUp。以下从源码角度详细解析其工作原理。
RateLimiter作为顶层类,主要通过静态工厂方法创建实例。它内部维护两个关键字段:
RateLimiter提供了三种工厂方法:
其中,permitsPerSecond表示每秒产生的令牌数量,warmupPeriod是预热阶段的时间,用于从慢速到快速平滑过渡。
SmoothBursty是RateLimiter的基础实现,主要用于平稳化消息的生产速率。其内部维护了以下字段:
SmoothBursty的构造过程如下:
public static RateLimiter create(double permitsPerSecond) { 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;} 构造时,传入stopwatch和maxBurstSeconds(默认为1秒),maxBurstSeconds限制了桶中令牌的最大持有时间。
setRate用于调整限流器的速率,实现如下:
public final void setRate(double permitsPerSecond) { checkArgument(permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive"); synchronized (mutex()) { doSetRate(permitsPerSecond, stopwatch.readMicros()); }} setRate首先校验参数,确保permitsPerSecond为正值。然后在同步块中执行doSetRate,调整稳定速率下的令牌生产间隔。
SmoothBursty的acquire方法用于获取令牌:
public double acquire() { return acquire(1);}public double acquire(int permits) { long microsToWait = reserve(permits); stopwatch.sleepMicrosUninterruptibly(microsToWait); return 1.0 * microsToWait / SECONDS.toMicros(1L);} acquire方法通过reserve获取所需的令牌数量,若桶中有足够的令牌,直接返回;否则,线程阻塞,直到获取到令牌。
reserve方法的核心逻辑:
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); return max(momentAvailable - nowMicros, 0);}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方法用于同步当前时间,更新桶中的令牌数量和下次获取时间。reserveEarliestAvailable方法根据当前时间决定是否需要等待,通过计算桶中令牌的存储情况,决定下一次获取令牌的时间。
SmoothWarmingUp在SmoothBursty的基础上增加了预热功能,以平滑地从低速率向高速率过渡。其内部维护了额外的字段:
SmoothWarmingUp的构造过程如下:
static RateLimiter create( double permitsPerSecond, long warmupPeriod, TimeUnit unit, double coldFactor, SleepingStopwatch stopwatch) { RateLimiter rateLimiter = new SmoothWarmingUp(stopwatch, warmupPeriod, unit, coldFactor); rateLimiter.setRate(permitsPerSecond); return rateLimiter;} 构造时,传入预热时间、时间单位、冷启动因子和stopwatch。
setRate方法与SmoothBursty类似,调整限流器的速率:
public final void setRate(double permitsPerSecond) { checkArgument(permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive"); synchronized (mutex()) { doSetRate(permitsPerSecond, stopwatch.readMicros()); }} doSetRate方法初始化稳定速率下的令牌生产间隔,并更新预热相关参数。
SmoothWarmingUp在resync方法中处理预热逻辑:
void resync(long nowMicros) { if (nowMicros > nextFreeTicketMicros) { double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros(); storedPermits = min(maxPermits, storedPermits + newPermits); nextFreeTicketMicros = nowMicros; }} coolDownIntervalMicros方法返回预热阶段的冷却间隔:
double coolDownIntervalMicros() { return warmupPeriodMicros / maxPermits;} 与SmoothBursty不同,SmoothWarmingUp的最大容量maxPermits可以大于permitsPerSecond,允许预热阶段的灵活配置。
SmoothWarmingUp的acquire方法与SmoothBursty类似,但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;} storedPermitsToWaitTime方法根据当前桶中的令牌数量和所需的令牌数量,计算需要等待的时间。该方法考虑了预热阶段的梯形区间和稳定速率区间,确保高频率的消息处理不会超过限流器的容量。
RateLimiter通过两种不同的实现策略(SmoothBursty和SmoothWarmingUp)满足不同的应用场景。SmoothBursty适合高频率的消息处理,通过简单的桶模型直接控制消息速率。而SmoothWarmingUp则在低频率下提供额外的预热阶段,平滑地从低速率过渡到高速率,避免因突发高负载而导致系统压力过大。两种实现都通过令牌桶算法,确保消息消费的均匀性和系统的稳定性。
转载地址:http://xlkkz.baihongyu.com/