那么怎么实现限流呢?
说到限流降级了,那就不能单纯的去针对 Redis 出现的问题而进行处理了,而实际上是为了保证用户保护服务的稳定性来进行的。
那么为什么要去限流呢?你要单纯的说是为了保证系统的稳定性,那面试官估计得崩溃,这和没说有啥区别,你得举个简单的例子才能正儿八经的忽悠住面试官,比如:
假设,我们当前的程序能够处理10个请求,结果第二天,忽然有200多请求一起过来,整整翻了20倍,这时候,程序就凉了,但是如果第一天晚上的时候,领导给你说,明天你写的那个程序大约会有200多个请求要处理,你这时候是不是得想办法,比如说,能不能再写出另外的一段程序来进行分担请求,这时候其实就相当于需要我们去限流了。
限流算法之漏桶算法
同样的,我们整个图来理解一下这个算法到底是怎么实现的。
如果一桶有一个细眼,我们往里面装水,可以看到水是一滴一滴匀速的下落的,如果桶满了就拒绝水滴继续滴入,没满的话就继续装水,实际上就是这样的水滴实际上就相当于是请求,如果水桶没满的时候,还能继续处理我们进来的请求,当水桶满了的时候,就拒绝处理,让他溢出。
前提是我们的这个桶是个固定的容器,不能随着水的增多桶会变大,要不然那还用什么限流算法。
简单的漏桶算法的实现:
public class LeakyBucket { public long timeStamp = System.currentTimeMillis(); // 当前时间 public long capacity; // 桶的容量 public long rate; // 水漏出的速度 public long water; // 当前水量(当前累积请求数) public boolean grant() { long now = System.currentTimeMillis(); // 先执行漏水,计算剩余水量 water = Math.max(0, water - (now - timeStamp) * rate); timeStamp = now; if ((water + 1) < capacity) { // 尝试加水,并且水还未满 water += 1; return true; } else { // 水满,拒绝加水 return false; } } }
上面的代码是来自悟空,不得不说,这个简单的例子虽然简单,但是把这个漏桶算法的简单原理描述的还是差不多的,而在这里最需要注意的,就是桶的容量,还有就是水桶漏洞的出水的速度。
既然我们了解了漏桶算法是如何实现限流的,那么必然也会有他处理不来的情况,因为我们已经定义了水漏出的速度,而这时候如果应对突发的流量忽然涌进来,他处理起来效率就不够高了,因为水桶满了之后,请求都拒绝了,都不处理了。
其实我们所说的漏桶算法还可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。
而我们的漏桶算法主要是能够强行限制数据的传输速率。
那么又有什么算法能够不进行强制限制传输速率,并且实现限流呢?
令牌桶算法
我们感谢百度,我从百度图片中找了个一个比较给力的图来描述令牌桶的算法。
令牌桶算法的基本过程是这个样子的:
- 用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中
- 假设桶最多可以存发b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃
- 当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌,并且数据包被发送到网络
- 如果令牌桶中少于n个令牌,那么不会删除令牌,并且认为这个数据包在流量限制之外
乍一看,怎么感觉这个令牌桶和漏桶这么像,一个是水滴,一个是令牌,实际上不是。
令牌桶这种控制机制基于令牌桶中是否存在令牌来指示什么时候可以发送流量。令牌桶中的每一个令牌都代表一个字节。如果令牌桶中存在令牌,则允许发送流量;而如果令牌桶中不存在令牌,则不允许发送流量。
而且他是能够应对突发限制的,虽然传输的速率受到了限制.所以它适合于具有突发特性的流量的一种算法。
而在 Google 开源工具包中的限流工具类RateLimiter ,这个类就是根据令牌桶算法来完成限流。大家有兴趣的可以去看看呀。
漏桶算法和令牌桶算法的区别
漏桶算法与令牌桶算法实际上看起来有点相似,但是不能混淆哈,这就是阿粉在上面说的:
- 漏桶算法能够强行限制数据的传输速率。
- 令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输
关于阿粉今天说的这些你学会了么?