高并发之初识限流
概述
高并发场景下,爆炸性大量的对数据库的请求操作不仅会占用十分高比例的网络带宽,导致其他应用对数据库的请求受阻,还会导致从库与主库的延迟大大增加,降低了从库数据的不准确率,也降低了缓存的命中率。
如下图:
限流方式
一般开发高并发系统常见的限流有:限制总并发数、限制瞬时并发数、限制时间窗口内的平均速率;其他还有如限制远程接口调用速率、限制MQ的消费速率。另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。以减少高并发对系统的影响,最终做到有损服务而不是不服务;限流需要评估好,不可乱用,否则会正常流量出现一些奇怪的问题而导致用户抱怨。 参考文章
其中限制总并发数:如数据库连接池、线程池;
限制瞬时并发数:如nginx的limit_conn模块,用来限制瞬时并发连接数;
限制时间窗口内的平均速率:如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率;
限流算法
- 计数器法
- 滑动窗口
- 漏桶算法
- 令牌桶算法
>计数器法
计数器法是最简单、最易实现的限流算法。通过重复设置计数器,对接口一定时间段内的访问频率进行限制。
弊端:存在临界问题。
如下图所示:
如上图所示,在临界的小时间段内,发送了200个请求,导致限流的不成功,可能会导致应用的崩溃。
>滑动窗口
滑动窗口可以被看做是一个高精度的计数器算法。其中小窗口的个数越多,对限流中请求的统计会越精确,但占用的系统资源会多。
如下图所示:
其中,虚线包括了6个小窗口,这该6个小窗口组成了一个滑动窗口,滑动窗口对请求数量进行限定;每个小窗口都有一个计数器,都限定了相同的一定时间。每经过该小窗口的时间,滑动窗口就向右侧移动一格,如上图的所示,从而避免了计数器法中的弊端。
>漏桶算法
漏桶算法(Leaky Bucket)作为计量工具(The Leaky Bucket Algorithm as a Meter)时,可以用于流量整形(Traffic Shaping)和流量控制(TrafficPolicing)。
其算法示意图如下:
漏桶算法构建一个容量固定的漏桶,请求数会先放入漏桶,以可控的一定速率流出来,当漏桶满了时,多余的请求会被丢弃。
>令牌桶算法
令牌桶算法可以看做是漏桶算法和滑动窗口思想的结合体,构造一个存放固定容量令牌的桶,按照可控的固定速率往桶里添加令牌。
如下图所示:
当桶满了时,新添加的令牌会被丢弃或拒绝。当一个请求过来时,该桶就移除一个令牌;当桶中没了令牌时,请求也就无法通过。其中移除令牌是没有延迟时间的,若当设置该延迟时间后,就十分近似漏桶算法了。它通过将桶总量划分为多个令牌的容量,不会造成大量请求的突发,可以很好地解决临界问题。
令牌桶算法与漏桶算法的比较
- 令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求;
- 漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝;
- 令牌桶限制的是平均流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿3个令牌,4个令牌),并允许一定程度突发流量;
- 漏桶限制的是常量流出速率(即流出速率是一个固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2),从而平滑突发流入速率;
- 令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率;
- 两个算法实现可以一样,但是方向是相反的,对于相同的参数得到的限流效果是一样的。