Guava限流设计下

228 阅读2分钟

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

前言

上一篇已经讲了限流算法中的漏桶算法,现在就讲一下令牌桶算法。

令牌桶算法

令牌桶算法(Token Bucket)和 漏桶算法效果一样但却是设计思路相反,且更加容易理解。不难发现他们就是一个通过有序的出水来控制请求速率,一个则通过有序的向桶内加入令牌控制请求速率和请求上限。

随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了。新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务。

令牌桶算法的另外一个好处是可以方便的改变接口请求速度,它是怎么实现的呢?

一旦我们需要提高速率,则按需提高放入桶中的令牌的速率即可。一般会定时(比如100毫秒)往桶中增加一定数量的令牌, 有些变种算法则会实时的计算应该增加的令牌的数量,非常的有效率。

1.png

2.png

引入pom.xml
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
 
 
private final static Logger LOGGER = LoggerFactory.getLogger(GuavaLimiterTest.class);
 
/**
* 设置了初始 桶容量 = 5 + 速率 每秒5个令牌
*/
private RateLimiter limiter = RateLimiter.create(5);
 
 
@Test
public void testLimiterCreate(){
while (true){
LOGGER.info("获取令牌={}",limiter.acquire());
}
}
 
#output
19:37:54.899 [main] INFO com.cases.guava.GuavaLimiterTest - 获取令牌=0.0
19:37:55.095 [main] INFO com.cases.guava.GuavaLimiterTest - 获取令牌=0.189775
19:37:55.296 [main] INFO com.cases.guava.GuavaLimiterTest - 获取令牌=0.197419
19:37:55.496 [main] INFO com.cases.guava.GuavaLimiterTest - 获取令牌=0.197053
19:37:55.694 [main] INFO com.cases.guava.GuavaLimiterTest - 获取令牌=0.197458
19:37:55.896 [main] INFO com.cases.guava.GuavaLimiterTest - 获取令牌=0.199117
19:37:56.098 [main] INFO com.cases.guava.GuavaLimiterTest - 获取令牌=0.197184

RateLimiter 是一个抽象类,SmoothRateLimiter 继承自 RateLimiter,不过 SmoothRateLimiter 仍然是一个抽象类,SmoothBursty 和 SmoothWarmingUp 才是具体的实现类。

3.png 含义:

stableIntervalMicros,稳定生成令牌的时间间隔(微秒),公式为:stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond。
maxBurstSeconds,能够存储多少秒生产的令牌,默认是``1``秒。
maxPermits,最大能够存储的令牌数,限流器随着时间推进,会不断的生产令牌,可以想象为有一个桶,用于存储产出过剩的令牌,但是桶有一个最大容量。计算公式:maxPermits = maxBurstSeconds * permitsPerSecond。
nextFreeTicketMicros,下个请求可以被授权令牌的时间(不管请求多少令牌),这个属性是实现当前请求的债务由下个请求来偿还机制的关键。
storedPermits,已存储的令牌,生产过剩的令牌会存储在桶内,但是小于等于maxPermits,这个值是SmoothBursty限流器能够应对突然爆发量请求的关键。生产令牌计算公式:newPermits = (nowMicros - nextFreeTicketMicros) / stableIntervalMicros。

请求令牌时序图(limiter.acquire())

4.png \