限流每个API接口都是有访问上限的,当访问频率或者并发量超过其承受范围时候,我们就必须考虑限流来保证接口的可用性或者降级,即接口也需要安装上保险丝,以防止非预期的请求对系统压力过大而引起的系统瘫痪 。
通常的策略就是拒绝多余的访问,或者让多余的访问排队等待服务,或者引流 。
限流算法常用的更平滑的限流算法有两种:漏桶算法和令牌桶算法 。
漏桶算法漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率 。示意图如下:
文章插图
可见这里有两个变量,一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate),伪代码如下:
double rate; // leak rate in calls/s因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流突发(burst)到端口速率 。因此,漏桶算法对于存在突发特性的流量来说缺乏效率 。
double burst; // bucket size in calls
long refreshTime; // time for last water refresh
double water; // water count at refreshTime
refreshWater() {
long now = getTimeOfDay();
//水随着时间流逝,不断流走,最多就流干到0.
water = max(0, water- (now - refreshTime)*rate);
refreshTime = now;
}
bool permissionGranted() {
refreshWater();
if (water < burst) { // 水桶还没满,继续加1
water ++;
return true;
} else {
return false;
}
【限流算法之漏桶算法、令牌桶算法】}
令牌桶算法令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样,但方向相反的算法 。随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了 。新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务 。
文章插图
大小固定的令牌桶可自行以恒定的速率源源不断地产生令牌 。如果令牌不被消耗,或者被消耗的速度小于产生的速度,令牌就会不断地增多,直到把桶填满 。后面再产生的令牌就会从桶中溢出 。最后桶中可以保存的最大令牌数永远不会超过桶的大小 。
传送到令牌桶的数据包需要消耗令牌 。不同大小的数据包,消耗的令牌数量不一样 。
令牌桶这种控制机制基于令牌桶中是否存在令牌来指示什么时候可以发送流量 。令牌桶中的每一个令牌都代表一个字节 。如果令牌桶中存在令牌,则允许发送流量;而如果令牌桶中不存在令牌,则不允许发送流量 。因此,如果突发门限被合理地配置并且令牌桶中有足够的令牌,那么流量就可以以峰值速率发送 。
Guava RateLimiter 简介google开源工具包Guava提供了限流工具类RateLimiter,该类基于令牌桶算法(Token Bucket)来完成限流,非常易于使用 。RateLimiter经常用于限制对一些物理资源或者逻辑资源的访问速率 。它支持两种获取permits接口,一种是如果拿不到立刻返回false,一种会阻塞等待一段时间看能不能拿到 。
RateLimiter和JAVA中的信号量(java.util.concurrent.Semaphore)类似,Semaphore通常用于限制并发量 。
源码中的一个例子,比如我们有很多任务需要执行,但是我们不希望每秒超过两个任务执行,那么我们就可以使用RateLimiter:
final RateLimiter rateLimiter = RateLimiter.create(2.0);另外一个例子,假如我们会产生一个数据流,然后我们想以每秒5kb的速度发送出去 。我们可以每获取一个令牌(permit)就发送一个byte的数据,这样我们就可以通过一个每秒5000个令牌的RateLimiter来实现:
void submitTasks(List<Runnable> tasks, Executor executor) {
for (Runnable task : tasks) {
rateLimiter.acquire(); // may wait
executor.execute(task);
}
}
final RateLimiter rateLimiter = RateLimiter.create(5000.0);另外,我们也可以使用非阻塞的形式达到降级运行的目的,即使用非阻塞的tryAcquire()方法:
void submitPacket(byte[] packet) {
rateLimiter.acquire(packet.length);
networkService.send(packet);
}
推荐阅读
- 编程中的基本数据结构与算法思想
- 抖音算法机制的好处 抖音是什么样的算法
- 抖音违规类型和原因有哪些,抖音违规操作被限流怎么办?
- 抖音怎么会限流?限流了应该怎么办 抖音限流怎么办?4步解决
- 百度架构师分享:通过分布式系统解决限流问题
- 抖音取消关注会不会影响流量 抖音一直点关注别人会不会被限流
- 小米|小米11 Ultra一度霸榜DXO!影像算法获权威机构认可
- 简单了解十大真实算法的特点
- 都在提的人工智能,其中的算法是什么?其实高中的知识就有
- 线性查找算法 程序员必知的十大基础实用算法之-BFPRT