现场是这样的。企业内网问答按客户端 IP 做每分钟 60 次限流,办公楼只有一个 NAT 出口,早会时间全员提问,网关日志里 429 一次刷出几百条,而单人本机测试怎么点都正常。

中间走过弯路,把「固定窗口在整点重置,早高峰正好撞上」当成主因,改完现象没变。

真正麻烦的地方在于,几个原因的现象几乎一样。限流键取了转发头最左值,实际是 NAT 出口地址;同一出口下的所有用户共用一份配额;没有按登录身份做加权,重用户被轻用户挤掉。

第一件事是确认网关拿到的限流键是什么

python -c "print({'xff':'203.0.113.7','user':'u_1024'})"

再往下把限流键换成登录态用户标识

python -c "print('key=user_id or token_hash')"

接着按用户维度用令牌桶限速

python -c "print('rate=1/s burst=60 key=user_id')"

末了统计 429 在各用户上的分布

python agg.py --log access.log --field status --group user_id

恢复的判定其实很直接。429 从整点集中变成长尾分布,人均失败率低于 0.5%,单个用户短时爆发仍然会被挡住。

这里还有个坑。内网按 IP 限流几乎必然误伤,NAT 后面到底有多少人不可知,先换成按身份限流再调阈值。报错文案容易误导,真正的原因往往在相邻那一层的日志里。

作者 9823189