限流算法

  1. 固定时间窗
    1. 比如1秒钟限制访问100次,则用1秒作为时间窗,用个计数器,下个时间窗到了就把计数器置0;实现方式可以用一个线程定时1秒钟刷一次,但在某些系统中,可能会有很多个qps拦截器,这样会导致线程数很多,所以也可以改成记录上次时间窗的时间点,每次计数器+1之前算一下时间窗是否超过1秒了。
  2. 滑动窗口
    1. 固定窗口有点不太好的是过于离散,会导致算错,例如1秒钟100次,在上一个时间窗的后500毫秒访问了99次,在下一个时间窗的前500毫秒依然可以访问99次,显然在两个时间窗临近的个500毫秒的总共1秒内,是超过了限制的了(最多超过2倍,也就是200次),改进的方式是把时间窗变小一点,例如把1秒变成10个100毫秒的时间窗,每次算的时候累加10个时间窗的结果。
    2. 一般可以用一个数组来表示,数组可以循环利用,用一个指针指向当前需要累加的数组元素,每经过100毫秒则移动指针到下一个元素(同样也可以改成计数的时候处理,避免产生很多线程)
  3. 日志
    1. 即使用滑动窗口,也一样是离散的,只是相比固定时间窗其力度更小了点,但在某些对精度要求很高的场景,是不满足的。可以采用基于日志的方式,每次请求过来的时候,把请求的时间戳记录下来,塞入队列中,当队列满了时候,看看头部元素是否超过了1秒,如果是,则pop头部,说明不会被限流;否则说明请求都是最近1秒钟内的而且队列满了,需要限流了。
  4. 漏桶
    1. 水流出来的速度就是服务能处理的速度,即使水很多,也是按照一样的速度流出来,需要考虑在很久没有请求来了的情况,要不要支持浮动弥补这段时间没有请求过来。实现上是算出一滴水需要处理多长时间,用一个变量表示上次请求的时间,如果新的请求和上次请求的时间少于一滴水的处理时间,则需要阻塞。
  5. 令牌桶
    1. 按照固定的速度往桶里放令牌,如果桶里有足够的令牌,就可以被处理。请求过来时,可以算一下桶里还有多少令牌(根据上次请求的时间到现在的间隔)
  6. 参考:
    1. https://dbwu.tech/posts/golang_ratelimit/
    2. https://github.com/uber-go/ratelimit
    3. https://github.com/juju/ratelimit  
posted on 2024-02-20 20:12  bytesmover  阅读(5)  评论(0编辑  收藏  举报