Rate limit 限流技术 在高并发系统中经常使用三种技术保护系统:缓存,降级和限流。缓存的目的是提升系统访问速度和增大系统能处理的容量,降级是当服务出问题是暂时屏蔽掉,等高峰期后或问题解决后在重启,然而针对有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀,抢购),写服务(评论,下单),频繁的复杂查询(评论的最后几页),这时候 需要限流这项技术来限制这些场景的并发/请求量。限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)
常见限流算法 (算法层面限流) 计数器, 令牌桶, 漏桶
计数器 主要用来限制总并发数,比如数据库连接池,线程池,秒杀的并发数;超过了瞬时请求数或者在一定时间段内的请求数设定的阈值,则进行简单粗暴的限流,主要针对的还是总体的请求数量,而不是考虑的平均速率的限流。
令牌桶 Token Bucket 随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务.令牌桶的另外一个好处是可以方便的改变速度. 一旦需要提高速率,则按需提高放入桶中的令牌的速率. 一般会定时(比如100毫秒)往桶中增加一定数量的令牌, 有些变种算法则实时的计算应该增加的令牌的数量 长期来看,符合流量的速率是受到令牌添加速率的影响,被稳定为:r 因为令牌桶有一定的存储量,可以抵挡一定的流量突发情况 M是以字节/秒为单位的最大可能传输速率。 M>r T max = b/(M-r) 承受最大传输速率的时间 B max = T max * M 承受最大传输速率的时间内传输的流量
Guava RateLimiter提供了令牌桶算法实现:平滑突发限流(SmoothBursty)和平滑预热限流(SmoothWarmingUp)实现。
漏桶 Leaky Bucket 漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率. 两桶对比 令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求; 漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝; 令牌桶限制的是平均流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿3个令牌,4个令牌),并允许一定程度突发流量; 漏桶限制的是常量流出速率(即流出速率是一个固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2),从而平滑突发流入速率; 令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率; 两个算法实现可以一样,但是方向是相反的,对于相同的参数得到的限流效果是一样的。 应用级限流 一般来说,对于一个应用系统都会有极限并发数和请求数,TPS和QPS。如果超过了阈值则系统会无法响应或者崩溃,所以一般在进行开发大规模系统的时候,要设置过载保护,以免大量请求使系统瘫痪。
限流总资源数。 有的资源是稀缺资源(如数据库连接、线程), 可以使用池化技术来限制总资源数:连接池、线程池。比如分配给每个应用的数据库连接是100,那么本应用最多可以使用100个资源,超出了可以等待或者抛异常。 如果接口可能会有突发访问情况,但又担心访问量太大造成崩溃,如抢购业务;这个时候就需要限制这个接口的总并发/请求数总请求数了;因为粒度比较细,可以为每个接口都设置相应的阀值。可以使用Java中的AtomicLong进行限流: try {
if(atomic.incrementAndGet() > 限流数) {
//拒绝请求 } //处理请求
} finally { atomic.decrementAndGet();
} 适合对业务无损的服务或者需要过载保护的服务进行限流,如抢购业务,超出了大小要么让用户排队,要么告诉用户没货了,对用户来说是可以接受的。而一些开放平台也会限制用户调用某个接口的试用请求量,也可以用这种计数器方式实现。这种方式也是简单粗暴的限流,没有平滑处理,需要根据实际情况选择使用; 2....