令牌桶算法是一种对请求限流的有效算法,核心思想是,一定时间内产生固定数量的令牌,拿到该令牌的请求可以通过,进行业务处理,没有拿到令牌的请求需要等待,直到新的令牌产生并领到该令牌,才可以通过,否则一直被阻塞或一定时间后拒绝。
令牌桶算法相比漏斗算法,更加灵活、可控制。在java中,Google的Guava中有对令牌桶算法的实现,我们可以直接拿过来使用:
1. 依赖
<!-- google开源工具类,包含令牌桶算法 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>
2. 使用
//创建令牌桶实例,30指一开始最大有30个令牌,并且每次生成30个
private RateLimiter rateLimiter = RateLimiter.create(30);
@GetMapping("/sale")
public AjaxResponse sale(Integer id){
AjaxResponse ajax = AjaxResponse.newSuccess();
try {
//1.没有获取到token请求一直直到获取到token令牌
//logger.info("等待时间 : " + rateLimiter.acquire()); //会一直等待
//2.设置一个等待时间,如果在等待的时间内获得到了token令牌,则处理业务,如果在等待时间内没有获取到token则抛弃
//最多等待3秒钟,3秒钟后还拿不到令牌就被抛弃
if(!rateLimiter.tryAcquire(3, TimeUnit.SECONDS)){
ajax.setData("失败 !当前请求被限流,直接被抛弃");
return ajax;
}
try {
//一些自己的业务
//............
ajax.setData("SUCCESS!");
return ajax;
}catch (Exception e){
e.printStackTrace();
return AjaxResponse.newInstance(500,e.getMessage());
}
}catch (Exception e){
e.printStackTrace();
return AjaxResponse.newInstance(500,e.getMessage());
}
}
我们可以看到核心的两个调用,一个是 rateLimiter.acquire() ,类似于悲观锁,没有拿到令牌的请求会一直被阻塞,直至拿到了令牌、另一个是 rateLimiter.tryAcquire(3, TimeUnit.SECONDS) ,指没有拿到令牌的请求最多等待3秒钟,3秒之后拿到令牌返回true,没有返回false,可以根据的业务场景进行开发。
相对于springcloud,有专门的 hytrix 和 sentinel 来进行限流和频率限制更加厉害。如果只是单纯普通的springboot项目,使用guava中的令牌桶算法来对请求进行限流是一种不错的方式。