常见限流算法

 

计数器算法(固定窗口):

当时间节点到达下一个阶段的时候重置允许通过的数量N

  实现:一段时间我们可以时间毫秒除单位时间 做为阶段标志来区分时间段,初始化计数器的时候设置容量为N,来一个请求N-1,N=0 的时候拒绝后面的请求,如果毫秒取模最为阶段标志变化的时候重置容量N开始下一个阶段

  优点:简单,并且可以限流

瞬时运行通过数变成2N

 

滑动窗算法:

  描述:相当于固定窗口细分成多个小窗口,每次移动一个小窗口,每次重置的小部分容量,这样通过的请求会更加平滑。

  实现:计数器算法的基础上,分成10 个阶段,用阶段标志/10,来区分小窗口,然后请求来了就计请求数量+1,这阶段的当前总容量动态计算 N/10*窗口小阶段编号。

  优点:比计算器算法平滑,

  缺点:滑动窗口还是有重置容量的问题,虽然分成了多个阶段重启,容量匀速增加。在小窗切换的过程中基本和令牌桶一样平滑,但是在切换到下一个大窗口的时候容量有一个重置的过程。总的来说没有令牌桶平滑。

 

备注:滑动窗和令牌桶平滑程度没有严格的界限,他们都差不多,令牌桶说的匀速增加容量不可能是绝对的匀速增加。它们两个平滑程度通过量没有严格的界限,只是显现方式不一样。

令牌桶算法:

  描述:向一个桶里面匀速的放入令牌,桶的容量有限,超过容量就不在放入,业务要执行钱需要去桶里拿到令牌,然后才能执行,如果桶空了,拿不到令牌,请求放弃。

一个后台线程用恒定的速度向 里面加入令牌,业务线程在队列的出口领取令牌 (正常不一定会做真的有令牌,只要令牌数量增加就行,和滑动窗口最大的区别在于是另外的线程添加令牌,没有划过大窗口时候重置容量的抖动 )

  优点:平滑的添加容量,瞬时最大允许通过数是N(相对计数器的瞬时情况来说是N+1)

相对漏桶,令牌桶桶只有限流作用,没用缓冲作用。

 

 

漏桶算法:

  描述:请求过来,入桶,痛的容量固定,桶满拒绝,桶下游个洞,恒定的速度漏漏出(处理请求)

然后别的线程恒定的速度异步的去消费漏下来的请求,为啥漏桶一般是异步的?因为入口速度任意,出口速度恒定,所以一般是异步的,当然算法死的人是活的,要等待然后同步返回也可以。

缓冲瞬时流量。

瞬时通过数是恒定下游线程处理速率个,瞬时接受的请求最多是N(相对计数器的瞬时情况来说是N+1),这个瞬时接受的请求是放在桶里面的,并没有真的执行。真正瞬间是下游决定的。对瞬时请求的处理太弱。

  

 

 

 

令牌桶限流类 RateLimiter(很多jar包都有他的实现),redisson有它支持集群的实现

  RateLimiter 的原理:

      1,RateLimiter 原理:下一次请求的时候,通过当前时间和上次生成最后一个令牌的时间算一个时间差,然算出这段时间应该生成多少令牌,然后计算可用牌数够不够,不够返回没有令牌(非阻塞),或者计算出一个令牌数量够了的时间点,睡眠等待时间达到然后返回(阻塞方式)
      2,另外 RateLimiter 有允许刚开始多发一小段时间(预消费 nextFreeTicketMicros )
      3,RateLimiter 精确到 每微秒 N个 令牌( double stableIntervalMicros; ),小于微秒以后就不是平每微秒都增加了

 

限流类Semaphore(Java自带),redisson有它支持集群的实现,它强调的一瞬间并发只能允许多少通过,而不是一定时间内能通过多少个,和前面的限流算法有本质的区别。

 

 

总结:计数器算法,滑动窗口算法,令牌桶算法,抛开实现方式,我们只谈通过情况他们是一类的

  单位指定后添加容量的是计数器算法

  单位时间中,阶段的添加容量的是滑动窗口,但是有大窗切换的容量重置

  单位时间中匀速的添加容量的令牌桶,没有容量重置。

  带有缓冲,更加适合业务允许异步的 是漏桶

  要求限制瞬时通过量,而不是单位时间通过量的 信号量(Semaphore)