[服务或接口限流算法1/2]-漏桶算法及令牌桶算法解析

本文详细介绍了限流在IT服务中的重要性,主要讲解了漏桶算法和令牌桶算法的工作原理、优缺点,以及它们在应对不同场景下的表现。通过实例对比,突出了两种算法的局限性和适用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

同字面意思一样,限流即为限制流量,已知服务并发上限,但是在某个时刻涌入了大量流量,服务器无法及时处理,则进入排队等待,从而导致服务超时或者响应时间过长,此时就需要我们的限流方案为这一现象进行一些处理, 目前主流的限流方案有以下几种,我们一起来实战演练一番

1.漏桶算法

原理及概念

漏桶限流算法是一种流量控制算法,它通过一个固定容量的桶来控制流出的速率。当流量到来时,先放入到漏桶中,如果漏桶已满则丢弃多余的流量,否则按照固定的速率流出。这样可以限制流量的突发性,平滑流量的输出。

漏桶限流算法的实现思路如下:

  1. 设定一个固定容量的漏桶,以及漏水的速率。
  2. 当有请求到来时,将请求放入漏桶中。
  3. 如果漏桶已满,则拒绝请求或者丢弃多余的请求。
  4. 按照固定速率处理漏桶中的请求。

在实际应用中,漏桶限流算法可以用于限制网络请求的速率,防止流量突发增大导致系统崩溃,也可以用于平滑流量的输出,保护系统资源。

如图所示

1-1

优缺点

优点

  1. 平滑输出:漏桶算法可以平滑地处理流量,防止突发流量对系统造成冲击,有助于保护系统稳定性。
  2. 控制速率:漏桶算法可以限制输出的速率,确保系统资源不被过度占用,有助于进行流量控制和保护。

缺点

  1. 丢弃请求:当漏桶已满时,漏桶算法会丢弃多余的请求,可能会导致部分请求被拒绝,对于某些应用场景可能不太友好。
  2. 无法应对突发流量:漏桶算法无法应对突发性的大流量,因为漏桶容量是固定的,超出容量的流量会被丢弃,无法灵活应对突发情况。

总的来说,漏桶算法适合用于平滑输出和控制速率,但在对于突发流量的处理上有一定局限性。在实际应用中需要根据具体场景和需求进行选择。

代码实现


java

复制代码

/** * 漏桶限流算法实现 * * @author AnkerEvans */ public class LeakyBucketCurrentLimitHandler implements CurrentLimitHandler { /** * 漏桶容量 */ private int capacity; /** * 漏水速率n/s, 每秒允许多少个请求 */ private int rate; /** * 当前水量 */ private int water; /** * 上次漏水时间 */ private long lastLeakTime; /** * 构建handler * * @param capacity 桶容量 * @param rate 流出速率(n/s) */ public LeakyBucketCurrentLimitHandler(int capacity, int rate) { this.capacity = capacity; this.rate = rate; this.water = 0; } /** * 尝试访问 * * @param tokens 需要获取令牌的数量 * @return 是否允许访问 */ @Override public synchronized boolean tryAccess(Integer tokens) { // 当前服务器时间戳 long now = System.currentTimeMillis(); // 求当前数量 water = Math.max(0, (int) (water - (now - lastLeakTime) / 1000 * rate)); // 更新上次流出时间 lastLeakTime = now; // 如果数量 + 需要获取令牌的数量 小于等于 桶容量大小, 则允许访问 if (water + tokens <= capacity) { water += tokens; // 漏桶未满,允许通过 return true; } // 漏桶已满,拒绝通过 return false; } }

2.令牌桶算法

原理及概念

令牌桶算法基于令牌桶的概念来实现流量控制。在令牌桶限流算法中,令牌桶以固定的速率往里面添加令牌,每个请求需要消耗一个令牌,如果令牌桶中的令牌数量不足,则请求将被限制或延迟处理。

令牌桶限流算法的基本原理如下:

  1. 令牌桶以固定的速率往里面添加令牌,直到达到最大容量为止。
  2. 每个请求需要消耗一个令牌,如果令牌桶中有足够的令牌,则请求可以被处理,同时从令牌桶中消耗一个令牌;如果令牌桶中没有足够的令牌,则请求被限制或延迟处理。
  3. 令牌桶中的令牌数量是有限的,当请求频率超过令牌桶的填充速率时,令牌桶将会耗尽,无法处理更多的请求。

令牌桶限流算法的优点包括:

  1. 平滑的流量控制:令牌桶算法可以以固定的速率向令牌桶中添加令牌,从而限制请求的处理速率,实现平滑的流量控制。
  2. 灵活性:令牌桶算法可以根据实际需求动态调整令牌的填充速率和容量,适应不同的场景。

然而,令牌桶限流算法也存在一些缺点:

  1. 突发流量处理:令牌桶算法对于突发流量的处理能力较差,因为令牌桶中的令牌数量是有限的,无法应对瞬时的大流量。
  2. 复杂性:相对于一些其他流量控制算法,令牌桶算法的实现可能相对复杂一些,需要考虑令牌的填充速率、容量等参数。

如图所示

TokenBucket

优缺点

优点

  1. 平滑的流量控制:令牌桶算法可以以固定的速率向令牌桶中添加令牌,从而限制请求的处理速率,实现平滑的流量控制。
  2. 灵活性:令牌桶算法可以根据实际需求动态调整令牌的填充速率和容量,适应不同的场景。

缺点

  1. 突发流量处理:令牌桶算法对于突发流量的处理能力较差,因为令牌桶中的令牌数量是有限的,无法应对瞬时的大流量。
  2. 复杂性:相对于一些其他流量控制算法,令牌桶算法的实现可能相对复杂一些,需要考虑令牌的填充速率、容量等参数。

代码实现


java

复制代码

/** * 令牌桶限流算法实现 * * @author AnkerEvans */ public class TokenBucketCurrentLimitHandler implements CurrentLimitHandler { /** * 令牌桶容量 */ private int capacity; /** * 当前令牌数量 */ private int tokens; /** * 上次填充令牌的时间 */ private long lastRefillTime; /** * 令牌填充速率 */ private int refillRate; /** * 构建令牌桶算法实现 * * @param capacity 令牌容量 * @param refillRate 填充速率 */ public TokenBucketCurrentLimitHandler(int capacity, int refillRate) { this.capacity = capacity; this.tokens = capacity; this.refillRate = refillRate; } /** * 补充令牌 */ private void refill() { long now = System.currentTimeMillis(); if (now > lastRefillTime) { int timePassed = (int) (now - lastRefillTime); /* 计算新token数量, 这段代码很好理解, 假设距离上次填充时间过去了 1000 ms, 填充速率每秒2个 那么需要填充的数量即为 1000 * 2 / 1000 = 2, 如果距离上次填充为500 ms, 则 500 * 2 / 1000 = 1, 间隔更小的话则不填充 通俗易懂的来讲就是, 按每秒生成令牌的速率填充令牌桶 */ int newTokens = timePassed * refillRate / 1000; // 令牌数量为 当前桶令牌数量 + 新生成的令牌数量, 与最大令牌容量取最小值即为当前令牌数量 this.tokens = Math.min(this.tokens + newTokens, capacity); this.lastRefillTime = now; } } @Override public boolean tryAccess(Integer token) { /* 相当于匀速填充令牌 */ refill(); /* 如果令牌数量大于需要获取的令牌数量,则通过,并且令牌数量-${token}个 相当于获取令牌的操作 */ if (this.tokens >= token) { this.tokens -= token; return true; } // 否则限制访问 return false; } }

小结一下:

了解完了漏桶限流和令牌桶限流算法,有没有发现他俩的区别

  1. 漏桶算法是限定单位时间内可以接受的最大请求次数.

    想想这样的方式是不是有一些缺陷,例如

    1. 该接口允许1秒内最大接收10个请求, 但是我在第100ms时已经放行了10个请求, 那么后面的请求将被拒绝

    2. 该接口允许1秒内最大接收1000个请求,但是恰好10ms内涌入了1000个请求,这种场景其实没有有效的实现对服务器资源的保护

    所以 漏桶限流算法存在很大的局限性, 读到这里,相信你也理解了漏桶算法的原理,希望在后续工作中,对于频繁访问或者流量不平滑的接口需要进行限流时,能对其一些核心参数进行优化, 避免出现上述的两种极端情况

  2. 令牌桶限流算法基于其实现原理,侧面的也解决了一些上文中漏桶算法的缺陷. 因为其相较于漏斗算法, 它对令牌的填充数量是匀速进行的

它们有一个共同的缺点: 无法应对流量激增这种情况,

我为两个限流算法各举一个简单的例子:

  1. 漏桶算法:

    1. 你开了一家早餐店, 每日早上限量100份供应,早上8:00开门9:00关门, 8:30的时候你早餐卖完然后关门走了, 慕名而来买早餐的人买不到早餐走了. 这种对应漏桶算法的缺陷a
    2. 你开了一家早餐店, 你每40分钟能做100份早餐, 你为了保险起见,预估了一下每小时卖100份, 结果刚开门2分钟就有100个人排队, 你拼老命10分钟做了80份然后累垮了, 这就是刚才提到的漏桶算法的缺陷b, 你的早餐店相当于服务器, 你40分钟做100份相当于你实际的处理速率, 然后那排队的100个人就是100个请求, 你累垮了则标识服务器宕机了
  2. 令牌桶限流算法:

    1. 你还是开了一家早餐店,你每10分钟只能做两份早餐, 来了十个人买早餐, 两个人买完早餐走了, 剩下的8个人等下一个10分钟的两份早餐,或者走人

作者:夜航猩
链接:https://juejin.cn/post/7315240062651056169
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值