事情本身不复杂,线索都在日志里。网关把令牌桶状态存在 Redis,重启脚本把桶初始化成 0,发布后 30 秒内所有请求都返回 429,监控上 QPS 从 200 掉到 0 再慢慢爬回。

第一反应是「多个实例启动时并发初始化互相覆盖」,验证下来不成立。

这类报错背后常见的原因有几处,逐个排除就行。初始化写入 0 而没有按容量预填。还有一种是桶容量设置得比每秒补充速率还小。

不容易想到的是,没有冷启动降级,重启即按满速限流。

先启动时按容量预填令牌

redis-cli SET rate:u1 100 EX 60

然后核对容量与补充速率的关系

python -c "print('capacity',100,'rate',50,'per_s')"

再往下加启动预热等待,避免重启瞬间放量

python -c "print('warmup sleep 5s then serve')"

等前面都确认过,再重启后立刻看健康检查与状态码

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/healthz

恢复的判定其实很直接。重启后前 10 秒 429 计数为 0,QPS 在 15 秒内回到重启前水平,Redis 里桶的初值等于容量。

容易忽略的是,令牌桶初值决定冷启动体验,重启前把桶状态持久化,比单纯调大容量更稳。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。

作者 9823189