限流系列之RateLimiter解析(一):SmoothBursty
限流系列之RateLimiter解析(一):SmoothBursty
一、简介
限流是服务治理的重要工具,在google的guava包里提供了关于速率限流器RateLimiter,可以帮助我们针对速率进行限流。
1. 类图
2. 原理
SmoothBursty是关于限流算法中令牌桶算法的一个实现,通过固定速率生成令牌,当流量进入时,申请令牌,令牌充足时则直接获取成功,不充足时返回等待时间。
需要注意的是,SmoothBursty支持预支令牌,如果本次申请令牌不足,可以直接对令牌进行预支,不会导致本次申请进行等待,而是影响下发申请。
二、创建和初始化
SmoothBursty的构造方法并没有对外开发,我们只能通过DateLimiter的create创建和初始化一个对象。
1. 成员变量
对象的创建本质上就是对成员变量的赋值,因此在介绍对象创建和初始化之前先介绍一下SmoothBursty的几个核心的成员变量。
基于我们对令牌桶算法的理解,其实我们大概也能揣测出SmoothBursty主要需要哪些字段:
令牌生成速率,最大令牌数,当前令牌数和当前时间。
下面我们验证一下对比和验证一下是不是和我们想象的一样。
//SmoothRateLimiter
/**
* The currently stored permits.
*/
double storedPermits;
/**
* The maximum number of stored permits.
*/
double maxPermits;
/**
* The interval between two unit requests, at our stable rate. E.g., a stable rate of 5 permits
* per second has a stable interval of 200ms.
*/
double stableIntervalMicros;
/**
* The time when the next request (no matter its size) will be granted. After granting a
* request, this is pushed further in the future. Large requests push this further than small
* requests.
*/
private long nextFreeTicketMicros = 0L; // could be either in the past or future
/**
* The underlying timer; used both to measure elapsed time and sleep as necessary. A separate
* object to facilitate testing.
*/
private final SleepingStopwatch stopwatch;
//SmoothBUrsty
/** The work (permits) of how many seconds can be saved up if this RateLimiter is unused? */
final double maxBurstSeconds;
如上面的代码,也有一些对应的注释,我们简单的过一遍
- SmoothRateLimiter类的成员变量
(1)storedPermits 库存的令牌数量
(2)maxPermits 可以容纳的最大令牌数量
(3)stableIntervalMicros 令牌生成的时间间隔,令牌生成速率的另一种描述。举个例子,我们一般用5个/秒来描述令牌生成的速度,那么对应stableIntervalMicros 就是 200毫秒/个
(4)nextFreeTicketMicros 最新的令牌生成时间,和storedPermits 是对应的,在这个时刻还有storedPermits 个库存令牌。
(5)stopwatch:计时器,用来计算时间 - SmoothBursty类的成员变量
SmoothBursty只有一个成员变量:maxBurstSeconds,和maxPermits 对应,在令牌不消费的情况下可以生成多长时间的令牌
2. RateLimiter#create
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(SleepingStopwatch.createFromSystemTimer(), permitsPerSecond);
}
@VisibleForTesting
stati