服务限流-令牌桶算法和漏桶算法
问题场景
在系统中,有时可能遭遇突发大流量来请求,这时如果请求量达到系统压力上限,就可能导致服务运行缓慢甚至宕机。此时我们的选项无非就是三板斧:缓存、限流、服务降级。
限流是对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机。常用的限流算法有令牌桶和和漏桶,而Google开源项目Guava中的RateLimiter使用的就是令牌桶控制算法。
漏桶算法
概念
思路很简单,请求先进入漏桶中,漏桶以恒定速度处理请求,如果堆积的请求超过了漏桶的容量,则溢出然后放弃处理或引流。
优点
- 系统处理能力恒定,能预防遭遇突发流量致使服务处理缓慢甚至宕机。
缺点
- 对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适。
令牌桶算法
概念
令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个或多个令牌,当桶里没有令牌可取时,则拒绝服务或引流(也可以阻塞等待令牌产生,注意这种阻塞不是公平的)。
RateLimter
- A rate limiter。每个acquire()方法如果必要的话会阻塞直到一个permit可用,然后消费它。获得permit以后不需要释放。
- RateLimiter在并发环境下使用是安全的:它将限制所有线程调用的总速率。注意,它不保证公平调用。
- Ratelimiter(直译为:速度限制器)经常被用来限制一些物理或者逻辑资源的访问速率。这和java.util.concurrent.Semaphore正好形成对照。
- 一个RateLimiter主要定义了发放permits的速率。如果没有额外的配置,permits将以固定的速度分配,单位是每秒多少permits。默认情况下,Permits将会被稳定的平缓的发放。
- 可以配置一个RateLimiter有一个预热期,在此期间permits的发放速度每秒稳步增长直到到达稳定的速率,SmoothBursty以稳定的速度生成permit,SmoothWarmingUp是渐进式的生成,最终达到最大值趋于稳定。
限流的其他一些问题
计数器算法
计数器算法则是算第一个请求的时间后的一分钟内,记录请求的次数,每个请求来了之后计数器+1。乍一想,没问题啊,把计数器放到Redis里面去,不久解决限流问题了吗?计数器达到临界值后就抛弃请求。一分钟后重置计数器。
但是这样的解决方案有一个问题:临界问题
举个例子:一个恶意用户,在12:00的时候发送100个请求,这样接下来一分钟内我们服务就会拒绝所有请求了,然后一分钟后12:01的时候,恶意用户继续发送100个请求,我们服务继续停止服务。