这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战
前言
临近双十一,经常会去看服务的运行状态。发现有一个基础服务一天总有几次cpu被拉满了。
分析发现接口请求量激增,因为这个接口是用来记录消息内容的,可以允许丢掉。为了保证服务的稳定,给上游服务增加限流器,在面对这种突发流量激增的情况,进行限流,减少调用基础服务的次数。
令牌桶
这种突发流量激增的情况,用令牌桶的好处是,一开始桶里会预留一些令牌,这样请求突然增多时,会先去把桶里的令牌消耗完,就起到了一个缓冲的作用。
令牌桶算法:
1,令牌按固定速率发放,生成的令牌放入令牌桶中。
2,令牌桶有容量限制,当桶满时,新生成的令牌会被丢弃。
3,请求到来时,先从令牌桶中获取令牌,如果取得,则执行请求;如果令牌桶为空,则丢弃该请求。
RateLimiter
guava的RateLimiter就是基于令牌桶算法实现的。
RateLimiter经常用于限制对一些物理资源或者逻辑资源的访问速率。与Semaphore 相比,Semaphore 限制了并发访问的数量而不是使用速率。(注意尽管并发性和速率是紧密相关)
通过设置许可证的速率来定义RateLimiter。在默认配置下,许可证会在固定的速率下被分配,速率单位是每秒多少个许可证。为了确保维护配置的速率,许可会被平稳地分配,许可之间的延迟会做调整。
可能存在配置一个拥有预热期的RateLimiter 的情况,在这段时间内,每秒分配的许可数会稳定地增长直到达到稳定的速率。
通过create方法创建限流器,参数是每秒允许通过的次数,等同于QPS
RateLimiter.create(permitsPerSecond)
请求进来时,先执行tryAcquire方法获取令牌,获得到了返回true,则进行业务逻辑,否则该请求就被限流拒绝了。
rateLimiter.tryAcquire()
这里为了能在一个接口内,针对不同业务方做分开限流,目的是为了不让一个业务方的调用量突然增大,导致其他的业务方也被限流了。用一个map集合去管理,key是业务方唯一标识,value是ratelimiter类。这样也可以根据不同业务方的QPS去设置不同的permitsPerSecond。