分布式限流常用算法

974 阅读3分钟

常见的限流方法有计数器、漏斗算法、令牌桶算法,各自也有各自的优缺点

一 计数器限流方法

计数器算法比较简单,即一段时间内的调用次数是有上限的,当大于最大请求次数,则拒绝服务 计数器算法伪代码:

public class CounterDemo {
    public long timeStamp = getNowTime();
    public int reqCount = 0;
    public final int limit = 100; // 时间窗口内最大请求数
    public final long interval = 1000; // 时间窗口ms
    public boolean grant() {
        long now = getNowTime();
        if (now < timeStamp + interval) {
            // 在时间窗口内
            reqCount++;
            // 判断当前时间窗口内是否超过最大请求控制数
            return reqCount <= limit;
        } else {
            timeStamp = now;
            // 超时后重置
            reqCount = 1;
            return true;
        }
    }
}

优点:原理简单,利于实现 缺点:

  1. 不能很好地防止瞬时流量,在01-02秒之间,请求假如都在1.1秒处进来,导致瞬时流量很大,所以使用计数器方法时,要预估好服务的瞬时承载能力
  2. 不能避免抖动问题 image.jpeg 如上图,假设1秒内最大请求数为100,所以在1-2秒之间最多只能有100次,在02秒时请求次数会被重置为0,重新计算,这样极端情况会在存在在1.5-2.5秒之间的请求总数是200

二 漏斗限流方法

漏斗算法思想是将所有请求先存到一个桶里。若此刻桶容量没满,表示当前请求是可以访问资源。若满了,则拒绝服务。同时桶会以固定速率取出桶里的请求来处理

具体实现方法可以将请求先暂存到一个队列中,若队列已满,则拒绝该请求。同时有一个周期性定时任务来消费队列里的数据

从实现可以看出,不管请求有多少或者瞬时流量有多大,请求的处理是固定速率的,所以令牌桶油流量整形的功能,具体实现代码可以参考:限流-令牌桶实现(go版本)

相对计数器方法,令牌桶能有效避免抖动的问题,但当瞬时请求量很大时,后续的请求很有可能由于得不到及时处理而超时

三 令牌桶方法

当一个请求进来时,它要先获取一个token,若获取成功,则可以访问,若不成功,则会拒绝服务。存放令牌的地方就是令牌桶,令牌桶中的令牌会以固定速率生成

具体的令牌桶伪代码:

public class TokenBucketDemo {
    public long timeStamp = getNowTime();
    public int capacity; // 桶的容量
    public int rate; // 令牌放入速度
    public int tokens; // 当前令牌数量
    public boolean grant() {
        long now = getNowTime();
        // 先添加令牌
        tokens = min(capacity, tokens + (now - timeStamp) * rate);
        timeStamp = now;
        if (tokens < 1) {
            // 若不到1个令牌,则拒绝
            return false;
        }else {
            // 还有令牌,领取令牌
            tokens -= 1;
            return true;
        }
    }
}

令牌桶能够承载一定的瞬时流量,并且也有一些广泛的应用,如guava中基于令牌桶算法实现的限流器,阿里出品的sentinel等都有使用令牌桶作为限流的实现

参考:

1. www.cnblogs.com/xuwc/p/9123…