为了保证API的可用性,以及系统的可靠性,需要为API限速。不然,API请求量大到系统无法处理时就会出现系统变慢,甚至宕机的情况。常见的限速场景:
请求限速
限制API在一秒中内能够处理的请求数量。如果超过这个数量,等待或者拒绝服务。通常情况下这个是首选。
并发限制
针对资源敏感的请求,比如CPU密集型API,进行并发限制,限制某一时刻最多只有有限个请求正在被处理。防止因为这些请求占用资源,导致其他请求得不到处理。
基于资源利用率限速
针对不同的请求分配了不同百分比的资源,当某一类请求超载时,对这类请求限速。
基于worker限速
这个是基于代码特征的限速。每类API通过不同的worker线程负责处理,当worker线程中出现请求堆积时进行限速。
http服务的话按照场景返回429或者503。
计数
单位时间内计数,超过这个数量时,拒绝服务,每个单位时间开始后计数清零。缺点是在时间边界处,会超过上限。比如,每秒限速100,在0.9s的时候来了100个请求全部得到处理,在下一秒0.1s来了100个请求。在0.9s到1.1s这个范围小于1s,但是请求达到了200。
0.1s 0.2s 0.3s 0.4s 0.5s 0.6s 0.7s 0.8s 0.9s 0.1s 0.2s
+----+----+----+----+----+----+----+----+----+----+----+---
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 100| 100| 0 | 0
+----+----+----+----+----+----+----+----+----+----+----+---
队列
请求过来的时候,先如队列,处理逻辑处理队列中的请求。
有一个容量固定的桶,桶中的请求以恒定的速率被处理。请求过来的时候,尝试进入桶,当桶满时被丢弃。本质上是队列后面加一个速率限制器。
漏桶还有一个变形,在漏桶前面加一个队列。当桶满的时候,先放入队列,这样可以保留一部分请求。
以恒定的速率向桶中加入token,当请求过来的时候从桶中获取token。如果桶空了,请求等待或者丢弃。相比漏桶令牌桶还可以做蓄水,当桶满的时候可以预留一部分token,可以做到突发(burst)的请求。
Guava中的RateLimter是一个平滑的基于令牌桶的实现,同时还实现了warm up特点的一个Ratelimiter。