6种限流方法之服务端漏桶算法

本文深入讲解漏桶算法,探讨其如何解决滑动时间窗口算法存在的问题,通过均匀速率处理请求,避免瞬时高峰导致的系统过载。文章还介绍了如何使用Redis的Redis-Cell模块实现漏桶算法,提供原子限流指令,实现分布式场景下的完美限流。

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

 原文地址:开发者导航 · 你想要的,我全都有!

漏桶算法

漏桶算法的思路来源于日常生活中常见的漏斗。

之前介绍过的滑动时间算法有一个很大的问题就是在一定范围内,比如 60s 内只能有 10 个请求,当第一秒时就到达了 10 个请求,那么剩下的 59s 只能把所有的请求都给拒绝掉,而漏桶算法刚好可以解决这个问题。

在漏桶算法中,无论上面的水流倒入漏斗有多大,也就是无论请求有多少,它都是以均匀的速度慢慢流出的。当上层的水流速度大于下层的流出速度时,漏斗就会慢慢充满,当漏斗满了之后就会溢出(丢弃)新来的请求;当上面的水流速度小于下面流出的速度时,漏斗永远不会被装满,也就可以一直流出。

漏桶算法的实现步骤是,

1)先声明一个队列用来保存请求,这个队列相当于漏斗,

2)当队列容量满了之后就丢弃(溢出)新来的请求,

3)声明一个线程定期从任务队列中获取一个或多个任务进行处理。

这样就实现了漏桶算法。

上面我们讲过的Nginx 的控制速率其实使用的就是漏桶算法,当然我们也可以借助 Redis 很方便的实现漏桶算法。

我们可以使用redis提供的 Redis-Cell 模块,该模块使用的是漏斗算法,并且提供了原子的限流指令,而且依靠 Redis 这个天生的分布式程序就可以实现比较完美的限流了。

Redis-Cell 实现限流的方法也很简单,只需要使用一条指令 cl.throttle 即可,使用示例如下:

> cl.throttle mylimit 15 30 60

1)(integer)0 # 0 表示获取成功,1 表示拒绝

2)(integer)15 # 漏斗容量

3)(integer)14 # 漏斗剩余容量

4)(integer)-1 # 被拒绝之后,多长时间之后再试(单位:秒)-1 表示无需重试

5)(integer)2 # 多久之后漏斗完全空出来

其中 15 为漏斗的容量,30 / 60s 为漏斗的速率。

谷歌/百度/微信搜索【开发者导航】网站或公众号,各类源码无套路,了解一下?

### 令牌桶算法桶算法对比 #### 实现原理 令牌桶算法(Token Bucket Algorithm)通过维持一个固定容量的令牌桶来控制数据包的发送速率。每当有新请求到来时,会尝试消耗一定数量的令牌;如果此时有足够的令牌,则允许该操作继续执行并减少相应数量的令牌。反之则拒绝服务或延迟处理直至获得足够的资源许可[^3]。 相比之下,桶算法(Leaky Bucket Algorithm)采用了一种更为严格的流量调控机制——想象有一个带有孔洞的容器持续向外渗水,当新的水流进入此装置内部时会被暂时储存起来等待缓慢流出。这意味着即使短时间内有大量的输入到达也只会按照固定的滴速排出,从而实现了平滑的数据流输出效果[^1]。 #### 主要差异 最显著的不同之处在于两者对于突发流量的支持程度: - **突发支持**:由于其设计特性,“令牌桶”可以容纳一定程度上的瞬时高峰负荷,在满足预设条件的情况下允许一次性释放较多量级的信息传递出去; - **严格限速**:“桶”的工作方式决定了它不具备这样的灵活性,始终保持着恒定不变的速度对外提供服务而不受外界因素干扰[^2]。 #### 应用场景 基于上述特点,两种方法适用于不同类型的业务需求: - 对于那些可能经历间歇性高并发访问压力的服务端接口而言,比如社交平台的消息推送功能或是电商平台促销活动期间的商品详情页加载过程,选用“令牌桶”策略往往更加合适因为这能兼顾性能优化的同时保障用户体验不受太大影响; - 而在网络通信领域内涉及到实时性强且对延时敏感的任务时(例如VoIP通话),为了确保音视频质量稳定可靠,则更倾向于依赖“桶”来进行精准的时间间隔调整以防止抖动现象发生。 ```python import time from threading import Thread, Lock class TokenBucket: def __init__(self, rate_limit, bucket_size): self.rate_limit = rate_limit # 每秒产生的token数目 self.bucket_size = bucket_size # token桶的最大容量 self.tokens = min(bucket_size, rate_limit * (time.time() % 1)) self.lock = Lock() def consume(self, tokens_needed=1): with self.lock: now = int(time.time()) if (now > self.last_time and self.tokens < self.bucket_size): elapsed = now - self.last_time new_tokens = elapsed * self.rate_limit self.tokens = min(self.bucket_size, self.tokens + new_tokens) self.last_time = now if self.tokens >= tokens_needed: self.tokens -= tokens_needed return True return False def leaky_bucket(data_rate, max_queue_length): queue = [] while True: data_incoming = yield from get_data() if len(queue) < max_queue_length: queue.append(data_incoming) processed_item = None if queue: processed_item = queue.pop(0) send_to_next_hop(processed_item) time.sleep(1 / data_rate) # Note: The above code snippets are simplified examples to illustrate the concepts. ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开发者导航

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值