限流的原理以及常用算法

限流的原理以及常用算法

     高并发的处理有三个比较常用的手段:缓存、限流和降级

     在一个分布式的高可用系统中,限流是必备的操作。这个流可以是:网络流量,带宽,每秒处理的事务数,每秒请求数,并发请求数,或者业务上的指标等。比如在参加一些秒杀活动的时候,我们可以看到,有时候会出现“系统繁忙,请稍后再试”或者 “请稍等”的提示,那这个系统就很可能是使用了限流的手段。

      限流是限制系统的输入和输出流量,以达到保护系统的目的。而限流的实现主要是依靠限流算法,限流算法主要有下面4种。

     1、固定时间窗口(计数器)

     计数器固定时间窗口算法是最基础也是最简单的一种限流算法。

     原理:对一段固定时间窗口内的请求进行计数,如果请求数超过了阈值,则拒绝该请求;如果没有达到设定的阈值,则接受该请求,且计数加1。当时间窗口结束时,重置计数器为0。

     优点:实现简单,容易理解

     缺点:限流策略过于粗略,不够平滑,无法应对两个时间窗口临界时间内的突发流量。

     举个栗子:

     假设这样一个场景,我们限制用户一分钟下单不超过 10 万次,现在在两个时间窗口的交汇点,前后一秒钟内,分别发送 10 万次请求。也就是说,窗口切换的这两秒钟内,系统接收了 20 万下单请求,这个峰值可能会超过系统阈值,影响服务稳定性。

     2、滑动时间窗口

     实现:滑动时间窗口算法是对固定时间窗口算法的一种改进,通过切分更细维度的计数器来记录一段时间窗口内请求数量,超过阈值拒绝请求。

     优点:准确性更高。

     缺点:没有从根本上解决临界问题。基于时间窗口的限流算法,不管是固定时间窗口还是滑动时间窗口,只能在选定的时间粒度上限流,对选定时间粒度内的更加细粒度的访问频率不做限制。

     3、漏桶算法

      思路:水(请求)先进入到漏桶里,漏桶以一定速度向外出水。当水流入速度过大,桶会直接溢出。即请求进入一个固定容量的Queue,若Queue满,则拒绝新的请求,可以阻塞,也可以抛异常。

      这种模型其实非常类似MQ的思想,利用漏桶削峰填谷,使得Queue的下游具有一个稳定流量。

       限流的原理以及常用算法

     实现:生产者和消费者

     优点:从出口处限制请求速率,并不存在上面计数器法的临界问题,请求曲线始终是平滑的。

     缺点:对请求的过滤太精准了,不允许任何的突发流量。比如我们限制每秒下单 10 万次,那 第10 万零 1 次的请求,就会被拒绝。大部分业务场景下,虽然限流了,但还是希望系统允许一定的突发流量,这时候就需要令牌桶算法。

     4、令牌桶算法

     思路:系统以一个恒定的速率往桶里放入令牌。桶的容量是一定的,如果桶已经满了就不再继续添加。若有请求需要处理,则从令牌桶里获取令牌,当桶里没有令牌,否则拒绝请求或者加入队列进行排队等等。

      限流的原理以及常用算法

 

     令牌桶算法并不能实际的控制速率。比如,10秒往桶里放入10000个令牌桶,即10秒内只能处理10000个请求,那么qps就是100。但这种模型可以出现1秒内把10000个令牌全部消费完,即qps为10000。所以令牌桶算法实际是限制的平均流速。具体控制的粒度以放令牌的间隔和每次的量来决定。若想要把流速控制的更加稳定,就要缩短间隔时间。

      实现:Redis+Lua

      优点:允许一定程度流量突发,但不会超过设置阈值,对用户友好同时有效保护系统。

      缺点:请求异步处理,无法同步返回

Google 的 Guava 中工具类 RateLimiter,就是基于令牌桶算法实现流量限制,使用非常方便。

 

     漏桶算法VS令牌桶算法

     1) 漏桶算法进水速率是不确定的,但是出水速率是一定的,当大量的请求到达时势必会有很多请求被丢弃。

     2) 令牌桶算法会根据限流大小,设置一定的速率往桶中加令牌,这个速率可以很方便的修改,如果我们要提高系统对突发流量的处理,我们可以适当的提高生成token的速率。

 

 

    参考链接:

    https://www.jianshu.com/p/6e4eb31128af

    https://juejin.cn/post/6870396751178629127

    https://www.cnblogs.com/shijiaqi1066/p/10508115.html

限流的原理以及常用算法

上一篇:Does C# have an equivalent to JavaScript's encodeURIComponent()?


下一篇:Centos7.2 Systemd 方式编译 Mysql5.7.11