免费软件制作网站模板下载软件,基本的网站建设步骤,高校工会网站建设,阿里巴巴的电子商务网站建设一文搞懂令牌桶限流#xff1a;平均速率与突发流量的本质#xff08;分布式令牌桶实现思路#xff09;
在做后端系统设计、网关限流或高并发控制时#xff0c;令牌桶#xff08;Token Bucket#xff09; 是一个绕不开的核心算法。很多人知道它“能限流”#xff0c;但真…一文搞懂令牌桶限流平均速率与突发流量的本质分布式令牌桶实现思路在做后端系统设计、网关限流或高并发控制时令牌桶Token Bucket是一个绕不开的核心算法。很多人知道它“能限流”但真正难理解的是这句话令牌桶限制的是平均处理速率但允许一定程度的突发流量这篇文章将基于一个完整的问答推导过程用时间线 场景的方式帮你一次性真正搞懂这句话背后的含义。一、先明确令牌桶到底在干什么令牌桶算法的核心思想可以总结为一句话系统以固定速率往桶中放入令牌请求只有拿到令牌才能被处理这里有三个关键要素令牌生成速率rate比如 100 个 / 秒桶容量capacity比如最多存 100 个令牌请求消耗令牌每个请求通常消耗 1 个令牌限流器本质上做的事情只有一件按照时间持续生成令牌并维护桶中令牌数量不超过上限当桶满时新生成的令牌会被直接丢弃。二、什么叫「限制平均处理速率」假设我们设置令牌生成速率100 token / 秒 桶容量100这意味着什么每 1 秒系统最多新增 100 个可用处理资格令牌10 秒内最多生成 1000 个令牌1 分钟内最多生成 6000 个令牌无论请求如何变化系统在较长时间尺度上的处理能力是被严格限制的也就是说你不可能让系统长期稳定地跑在 200 QPS、300 QPS。这就是所谓的限制的是平均处理速率三、那什么是「允许突发流量」关键就在于令牌是可以提前积累的。我们还是用上面的参数通过时间线来看。四、完整时间线推演核心理解阶段 1系统启动后的 1 秒内没有请求t 0s ~ 1s限流器持续生成令牌桶容量是 100结果桶中令牌 100已满阶段 2t 1s瞬间来了 100 个请求注意关键词瞬间可能是 1ms 内处理结果每个请求消耗 1 个令牌桶中正好有 100 个令牌100 个请求 → 全部立即通过 桶中令牌 0这一步非常重要虽然配置的是 100 QPS但这一刻的并发远远超过了 100 QPS这就是允许突发流量五、这为什么不违反「100 QPS」因为QPS 是一个“时间平均值”概念。我们看一个完整时间段时间段处理请求数第 0~1 秒0第 1~2 秒100合计100平均下来100 / 2 50 QPS完全没有超过限制。令牌桶限制的是长期平均速率而不是瞬时峰值。六、突发之后会发生什么阶段 3突发请求消耗完令牌后t 1s 桶中令牌 0此时限流器仍然在工作按速率继续生成令牌100 token / 秒 ≈ 每 10ms 生成 1 个阶段 4这时又瞬间来了 100 个请求假设只过去了 10ms桶中令牌 ≈ 1这 100 个请求会怎样七、两种工程实践结果非常重要情况一不允许等待最常见于网关1 个请求拿到令牌 → 立即通过剩余 99 个请求 → 直接被限流返回 429此时你的理解“只能一个过去”是完全正确的。情况二允许等待服务内部常见1 个请求立即通过剩余 99 个请求进入等待队列后续每生成 1 个令牌就放行 1 个请求最终表现为输出速率被平滑成 100 QPS瞬时峰值被削掉请求被“平滑接住”八、那「允许一定程度」到底是多少答案只有一个桶容量capacity桶容量 100 → 最多允许瞬间 100 个请求桶容量 1000 → 最多允许瞬间 1000 个请求公式级理解最大突发能力 桶容量九、和漏桶算法的一句话对比很多人会把令牌桶和漏桶混在一起这里给你一句话区分令牌桶限制平均速率允许突发漏桶限制输出速率不允许突发。十、一句话总结你可以这样总结令牌桶令牌桶算法通过限制令牌的生成速率来约束系统的长期平均处理能力同时允许在桶容量范围内消耗提前积累的令牌从而在短时间内承受一定规模的突发请求当令牌耗尽后请求将只能以固定速率被放行或直接被限流。如果你理解到这里说明你已经真正理解了什么叫“限制平均速率但允许突发流量”十一、Redis 分布式令牌桶实现思路工程实践在单机内存中实现令牌桶并不复杂但在多实例部署微服务 / 网关集群的场景下就必须借助 Redis 来实现分布式限流以保证多节点之间的令牌一致性。下面给出一个经典且工程上可落地的 Redis 分布式令牌桶设计思路。1、 核心设计目标使用 Redis 实现令牌桶需要解决三个问题令牌全局唯一多个实例共享同一个桶原子性生成令牌 消耗令牌不能被并发破坏高性能限流逻辑必须非常快因此Lua 脚本 Redis 单线程原子执行是最优解。2、 Redis 中需要维护的关键数据通常使用一个 Redis Key 表示一个令牌桶例如rate_limiter:{api_name}该 Key 对应的数据结构中需要维护tokens当前桶中剩余令牌数last_timestamp上一次补充令牌的时间戳逻辑上可以理解为(tokens, last_timestamp)3、 请求到来时的完整执行流程每个请求到来都会在 Redis 中原子执行以下步骤获取当前时间now根据时间差计算应补充的令牌数new_tokens (now - last_timestamp) * rate更新桶中令牌数不能超过容量tokens min(capacity, tokens new_tokens)判断是否有足够令牌如果tokens 1tokens tokens - 1请求放行否则请求被限流更新last_timestamp now以上逻辑必须在一个 Lua 脚本中完成否则并发下会出现超发令牌的问题。4、 Lua 脚本伪代码示例下面是一个简化版伪代码便于理解思路localratetonumber(ARGV[1])-- 每秒生成令牌数localcapacitytonumber(ARGV[2])-- 桶容量localnowtonumber(ARGV[3])localtokenstonumber(redis.call(HGET,KEYS[1],tokens))orcapacitylocallasttonumber(redis.call(HGET,KEYS[1],last))ornowlocaldeltamath.max(0,now-last)localnew_tokensdelta*rate tokensmath.min(capacity,tokensnew_tokens)iftokens1thenredis.call(HSET,KEYS[1],tokens,tokens)redis.call(HSET,KEYS[1],last,now)return0elsetokenstokens-1redis.call(HSET,KEYS[1],tokens,tokens)redis.call(HSET,KEYS[1],last,now)return1end返回值1→ 请求通过0→ 请求被限流5、 为什么 Redis 令牌桶是“软限流”Redis 令牌桶本质上是立即判断是否有令牌不负责请求排队因此它通常用于API 网关限流外部接口防刷秒杀前置拦截是否“等待令牌”通常由客户端重试上层业务队列来实现而不是限流器本身。6、 工程实践中的几个关键细节时间单位统一建议统一使用毫秒时间戳桶初始化第一次访问时默认令牌为capacityKey 过期策略可设置较长 TTL避免冷 Key 堆积限流粒度按接口限流按用户限流按 IP 限流7、 一句话工程总结Redis 分布式令牌桶通过 Lua 脚本保证限流逻辑的原子性使多实例系统在共享令牌的情况下依然能够严格控制整体平均速率同时保留令牌桶对突发流量的天然支持能力。