用令牌生成速率控制平均流量,用桶容量吸收突发流量,再用拒绝策略兜底,三者匹配业务峰值才算合理。
接口限流最怕两件事:平时限得太死,正常用户被误伤;大促一来又限不住,下游被打穿,令牌桶算法正好卡在中间:桶里攒令牌,请求来了取令牌,取不到就拒绝或排队,匀速发令牌管住长期平均速率,桶容量留给短时突发,下面从参数、代码、分布式落地到成本评估,把这件事拆开讲清楚。
令牌桶算法接口限流怎么实现:参数、代码与落地路径
令牌桶的三个核心参数怎么定
令牌桶不是配一个速率就完事,三个参数必须一起看:
- 令牌生成速率:决定长期平均放行能力,按单实例压测得出的安全QPS分配,再按集群实例数折算,下游是数据库时,速率要跟着连接池和慢查询水位走。
- 桶容量:决定能吸收多少瞬时突发,容量太小,正常刷新和重试会被误杀;容量太大,突发会把下游压垮,常用估算方式是“平均速率 × 可容忍突发秒数”,再通过压测修正。
- 拒绝策略:取不到令牌时是快速失败、排队等待,还是降级返回,核心交易接口适合快速失败加友好提示,查询类接口可以短时间排队。
| 参数 | 作用 | 配置偏保守的后果 | 配置偏激进的后果 |
|---|---|---|---|
| 令牌速率 | 控制平均流量 | 正常请求被限 | 下游过载 |
| 桶容量 | 吸收突发流量 | 秒杀开场大量误伤 | 瞬时洪峰打穿依赖 |
| 拒绝策略 | 决定用户体验 | 排队拖高延迟 | 直接报错流失用户 |
单机令牌桶的实现路径
Java服务常用Guava的RateLimiter做单机限流,操作路径不复杂:
- 引入Guava依赖。
- 在接口入口创建RateLimiter,速率按单机安全QPS设置。
- 请求到达时调用tryAcquire,拿不到令牌直接返回限流响应。
RateLimiter limiter = RateLimiter.create(50.0); // 每秒50个令牌
if (!limiter.tryAcquire()) {
return Response.tooManyRequests();
}
Nginx层也能做粗粒度令牌桶。limit_req_zone定义共享内存和速率,limit_req里的burst对应桶容量,nodelay表示突发请求立即处理而不是排队:
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ {
limit_req zone=api burst=20 nodelay;
}
这段配置的意思是:每个IP平均每秒10个请求,最多允许20个突发,超过就返回503,注意burst不是越大越好,它直接决定后端能承受的瞬时压力。
分布式令牌桶的落地方式
单机令牌桶在集群里会失真:10台机器各放50个令牌,实际放行能力变成500,分布式场景通常用Redis加Lua脚本保证原子性,操作路径如下:
- 以接口名或租户ID作为Redis key。
- Lua脚本里读取上次刷新时间和当前令牌数。
- 按时间差补充令牌,上限不超过桶容量。
- 判断令牌足够则扣减并返回1,否则返回0。
- 用Redis TIME取服务端时间,避免客户端时钟漂移。
local key = KEYS[1]
local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local data = redis.call('HMGET', key, 'tokens', 'last')
local tokens = tonumber(data[1]) or capacity
local last = tonumber(data[2]) or now
local delta = math.max(0, now - last)
tokens = math.min(capacity, tokens + delta rate)
if tokens >= 1 then
tokens = tokens - 1
redis.call('HMSET', key, 'tokens', tokens, 'last', now)
return 1
else
redis.call('HMSET', key, 'tokens', tokens, 'last', now)
return 0
end
Spring Cloud Gateway可以在网关层直接挂RedisRateLimiter,关键参数是replenishRate、burstCapacity和requestedTokens。replenishRate对应令牌速率,burstCapacity对应桶容量,配置时把路由ID和限流器绑定,网关就会在转发前完成扣减。
令牌桶与漏桶算法限流区别:高并发场景怎么选
两种算法的行为差异
令牌桶和漏桶经常被拿来比较,但它们的性格完全不同:
- 令牌桶:允许桶里攒令牌,短时突发可以一次取走多个令牌,输出速率有弹性。
- 漏桶:请求先进桶,再以恒定速率流出,输出非常平滑,但突发只能排队或丢弃。
| 对比项 | 令牌桶 | 漏桶 |
|---|---|---|
| 突发流量 | 允许短时突发 | 严格平滑输出 |
| 平均速率 | 由令牌生成速率控制 | 由漏水速率控制 |
| 实现复杂度 | 中等,需处理令牌补充 | 较低,队列加定时消费 |
| 典型场景 | API网关、秒杀入口、查询接口 | 下游脆弱系统、消息发送、写库保护 |
行业共识认为,面向用户的接口入口更适合令牌桶,因为用户行为天然有突发;面向数据库和第三方服务的出口更适合漏桶,因为下游需要稳定压力,这个选择不是技术炫技,而是把弹性留在入口,把确定性留给下游。
高并发场景下的选择路径
判断路径可以简化成三个问题:
- 业务能不能接受短时突发?能,优先令牌桶。
- 下游能不能承受瞬时洪峰?不能,令牌桶桶容量要收紧,或在出口再加漏桶。
- 被限流的请求是快速失败还是排队?核心交易快速失败,异步任务可以排队。
电商秒杀就是典型:开场瞬间流量陡增,令牌桶用桶容量吸收第一波,再用速率限制持续放行,漏桶如果放在入口,会把大量请求压进队列,延迟飙升,用户体验反而更差。
电商秒杀场景下令牌桶限流配置实战
秒杀接口的分层限流设计
秒杀接口不能只靠一层限流,常见做法是三层配合:
- 网关层:按IP、用户ID、接口维度做令牌桶粗筛,挡住明显异常的刷单流量。
- 应用层:按商品ID或活动ID做细粒度令牌桶,保护库存扣减和订单创建。
- 数据层:数据库连接池、Redis连接数设硬上限,作为最后一道闸门。
网关层桶容量可以适当放大,因为要容忍用户刷新和网络重试;应用层桶容量要紧跟库存和下游处理能力,不能照搬网关参数。
杭州互联网公司接口限流实践中的参数调优
杭州不少电商团队的做法是先压测再定参,步骤很实在:
- 单机压测得到接口安全QPS,记录P99延迟和错误率拐点。
- 按集群实例数折算总速率,再留出一定余量给流量倾斜。
- 桶容量按“允许的突发倍数”设置,通常用平均速率的数倍做起点。
- 上线后观察通过率、拒绝率、下游连接池水位,按小时级调整。
监控指标至少包括:令牌桶拒绝数、接口P99、Redis扣减耗时、下游错误率,拒绝率突然升高不一定是限流太严,也可能是上游重试放大,把网关日志里的限流原因打全,排查会快很多。
避免令牌桶误伤的三个细节
- 区分维度:按用户、IP、接口、租户分别建桶,不要一个全局桶打天下。
- 设置白名单:内部服务调用、健康检查、支付回调走独立通道,避免被普通用户流量挤掉。
- 动态调整:大促前临时抬高桶容量,活动结束恢复;配置变更要走灰度,不要一把梭。
分布式接口限流中令牌桶的落地细节与成本评估
Redis加Lua保证原子性
分布式令牌桶的核心是“读令牌、补令牌、扣令牌”三步原子完成,用Lua脚本把逻辑放在Redis端执行,避免并发竞争,操作时注意:
- key要设置过期时间,避免冷接口长期占内存。
- 时间戳用Redis服务端时间,不要用应用服务器时间。
- 脚本先用
SCRIPT LOAD加载,再用EVALSHA调用,减少网络传输。 - 对热点key做分片,比如按用户ID哈希到多个桶。
令牌桶限流组件成本怎么评估
限流组件的成本不只是软件授权,自研、商用API网关、云服务三种路线各有账本:
| 路线 | 主要成本项 | 适合团队 |
|---|---|---|
| 自研Redis加Lua | 开发人力、Redis资源、监控运维 | 有中间件团队、流量模型稳定 |
| 商用API网关 | 授权费用、实施成本、绑定厂商 | 需要快速上线、合规要求高 |
| 云服务限流 | 按量计费、网络调用、配置管理 | 弹性业务、不想维护基础设施 |
业内专家指出,限流组件的隐性成本常被低估:参数不合理导致的误限流、Redis热点带来的延迟、大促前临时改配置的沟通成本,往往比软件本身更贵,较大比例团队最终选择“自研令牌桶加Redis兜底”,因为可控性高,排查链路短。
高可用与降级策略
Redis故障时限流不能跟着瘫,常用兜底方案:
- 本地令牌桶作为第一层,Redis分布式桶作为第二层。
- Redis超时后快速降级到本地限流,并打点告警。
- 核心接口保留静态阈值,配置中心推送到本地缓存。
- 集群限流和单机限流同时存在时,取更严格的结果,避免双重放行。
令牌桶算法接口限流常见问题Q&A
令牌桶容量设置多少合适?
容量等于业务允许的瞬时突发请求数,常用估算方式是平均速率乘以可容忍突发秒数,再结合压测修正,容量过大失去保护意义,容量过小会误伤正常刷新和重试。
令牌桶和漏桶在网关限流中怎么选?
需要容忍突发选令牌桶,需要平滑输出选漏桶,API网关多数场景用令牌桶做入口粗筛,下游数据库保护可以在出口再加漏桶,两者不是互斥关系,分层使用更稳。
分布式令牌桶如何避免Redis热点?
按接口、租户或用户ID分片key,本地预取部分令牌,Redis只做兜底扣减;Lua脚本保证原子性,同时监控单key的QPS和耗时,Redis单key的QPS上限受实例规格和网络往返影响,分片是常规手段。
令牌桶限流的关键不在算法名字,而在速率、容量、拒绝策略是否匹配业务突发特征,先单机压测定基线,再分布式扩展,最后用监控闭环调参,接口限流才算真正落地。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/784748.html


