开始前,会这些就够了
- 理解每秒速率
遇到不熟悉的词,先看这 3 个解释
- 缓存击穿
- 一个被集中访问的热点数据失去有效缓存时,许多请求同时去读取或重建它,突然把压力转给后面的服务;重点是热点与并发重建叠在一起。
- 并发
- 多项任务在同一段时间内处于推进或等待中,执行过程可以交错,也可能在多个核心上同时进行;并发强调任务的重叠,不要求每个瞬间都一起执行。
- 限流
- 按用户、接口或服务等范围,限制一定时间内允许进入的请求或工作量;超过规则的请求会被拒绝、延后或排队,具体怎样处理要由系统说明。
假设一个接口的令牌桶每秒补充两个令牌,最多存三个,开始时已经装满。你在同一时刻连续发三个请求,竟然全部通过。这个结果并不矛盾:每秒补两个描述补充速度,桶里原本还存着三次机会。它不是“任意一秒最多两个请求”的严格窗口规则。
桶里有令牌,就能通过吗?
连续发出请求,观察短时突发与持续速率的区别。
- 模拟时间
- 0.0 秒
- 剩余令牌
- 3.0
- 已放行
- 0
- 已拒绝
- 0
固定容量 3、每秒补充 2 个令牌,每次请求消耗 1 个。时间由按钮推进。它是单桶学习模型,不模拟多实例共享状态、原子操作或真实网关。
容量和速率各管一件事
每个请求消耗一个令牌,足够就放行,不足就按策略拒绝或等待。补充速率决定持续压力能以多快的速度获得新额度;桶容量决定空闲后最多能攒多少额度,因而影响突然到来的流量能一次通过多少。桶满时继续补充的额度会丢弃,不会无限积存。
在本篇连续补充、每请求耗费一个令牌的模型里,一段长为 T 秒的区间,最多能使用区间开始时的余额再加 rT,余额不超过容量 B。因此 B + rT 是一个上界,不是每段都能达到的承诺。不能只看补充速率,就说短时间内绝不超过那个数。
不用真的等待,也能观察额度恢复
程序用人为给定的时刻代替真实时钟。前三次在零秒发生的请求取完余额,第四次被拒绝;半秒后补回一个令牌,一秒时又补回一个。代码按时间差计算余额,不需要真的创建令牌对象,也没有任何网络请求。
这里是单线程、单桶、连续补充模型,时刻必须不倒退。实际实现可能每隔一段时间批量补充,短时边界会不同;并发调用还要把读取余额、补充与扣除作为一个不可被其他请求插入的整体,否则两个请求可能花掉同一份额度。
capacity = 3
rate = 2
tokens = float(capacity)
last = 0.0
for now in [0, 0, 0, 0, 0.5, 1]:
assert now >= last
tokens = min(capacity, tokens + (now - last) * rate)
last = now
allowed = tokens >= 1
if allowed:
tokens -= 1
print(now, allowed, round(tokens, 2))
怎样运行:Python 3.10+;保存为 bucket_demo.py,运行 python bucket_demo.py。
运行后对照结果
0 True 2
0 True 1
0 True 0
0 False 0
0.5 True 0.0
1 True 0.0
先说清所有请求是不是共用一个桶
按用户、接口、商品或服务实例分桶,限制的是不同对象。两个实例各自有每秒两个额度,并不会自动组成全站每秒两个的额度。Envoy 文档会说明本地桶的进程或连接范围;Sentinel 的热点参数限流则展示按参数值进行限制的用法。看配置时先读范围,再读数字。
成本也不一定是一请求一令牌。一次导出可能比一次查详情昂贵,设计可为操作定义不同费用;但请求费用大于桶容量时,它永远无法攒到足够额度,需要明确拒绝或换规则。公平地分配额度和防止某个用户独占,也不是单桶公式自动提供的功能。
通过得不快,也可能同时堆着很多请求
限流控制进入速度,不直接限制同时执行的数量。如果每个请求突然变得很慢,即使每秒只放少量请求,在途任务仍可能累积。保护服务时还要观察执行时长、并发数量和队列长度,必要时组合并发上限与排队期限。
被拒绝后立刻循环重试,会继续消耗客户端与网关资源。应该按接口规则等待、设置总预算,并让用户知道未完成状态。阿里云的中文限流说明也区分持续速率与存量突发;其中的默认桶系数是产品配置,不能当作所有令牌桶天然都一样。
记住这三点
- 补充速率限制长期新增额度,容量决定可积攒的突发额度。
- 短时间上界包含初始余额,不能只计算
rT。 - 分桶范围、请求费用和并发控制都需要单独说明。
别把例子用过头
- 程序不是线程安全或集群共享的生产限流器。
- 连续补充与定时批量补充的短时行为可能不同。
- 限流并不保证请求耗时,也不保证每个用户公平获得额度。
自己试一小步
把初始余额改成零,和把容量改成零,有什么不同?
- 分别修改初始 tokens 和 capacity,其他条件保持不变。
- 再设计一组带长空闲的请求时刻,观察恢复上限。
- 写出两个独立实例共享全局额度时还缺少什么。
做到这些,就算完成
- 能区分启动余额与最大容量。
- 余额不会因为长时间空闲超过容量。
- 不把两个本地桶误称为同一个全局限额。
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- 限流算法选择 (新标签页)↗
直接阅读令牌恒速补充、存量桶容量、突发流量与单机模拟集群限制;未推广该产品默认系数。
- Envoy v1.35.0: Local rate limit (新标签页)↗
支持 HTTP 本地令牌桶、拒绝行为和按进程或连接确定作用范围。
- 热点参数限流 (新标签页)↗
直接阅读 Overview:按商品或用户等热点参数进行流控,并结合令牌桶;不提供未经版本核验的依赖配置。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
Bucket4j:把限流速度与突发容量分开看 (新标签页)↗
先画一个令牌桶的纸上时间线,比较初始容量、逐步补充与整批补充;取不到额度就拒绝。
查看学习准备与访问说明 →