一句话定义
限流(Rate Limit)是服务端给自己装的护栏——单位时间内只接这么多请求,超了就回一个 429 Too Many Requests;指数退避(Exponential Backoff)是客户端的礼貌——被拒之后不立刻再撞,而是把等待时间成倍拉长,再带一点随机抖动。
429 不是故障,是"你太快了"
限流一般用令牌桶(AI 词典:Token">Token Bucket)或滑动窗口实现。想象一家奶茶店每小时只发 100 个号,店员手里攥着一把令牌,来一个客人给一个,给完就挂"暂停营业"。牌子就是 429。它传递的信息不是"我挂了",而是"我还在,慢点来"。这个区别很重要:故障要绕路,限流只要等。
退避怎么算
最朴素的退避是第 n 次失败等 base × 2^n:1 秒、2 秒、4 秒、8 秒、16 秒……必须设上限(cap),否则第 20 次重试要等十几天,等于把任务悄悄埋了。上限一般取几十秒,并且总重试次数也要有封顶。
抖动(Jitter)才是命门
假设一千个客户端在同一毫秒收到 429,它们都规规矩矩等 1 秒、2 秒、4 秒——于是它们会在同一毫秒再次一起冲上来。服务端刚喘口气又被拍死,所有人等更久,然后再一起冲。这叫重试风暴(Retry Storm)或惊群(Thundering Herd),节奏性雪崩,比一开始不重试还惨。
抖动就是给每个人的等待时间掺入随机量,把这一千个人打散。
| 策略 | 第 n 次等待 | 问题 |
|---|---|---|
| 固定间隔 | 恒定 1 秒 | 等于自己 DDoS 对方 |
| 纯指数退避 | base × 2^n | 单客户端友好,多客户端会同步 |
| 等抖动 Equal Jitter | 一半固定 + 一半随机 | 保底等待,又打散 |
| 全抖动 Full Jitter | random(0, base × 2^n) | 打散最彻底,工程上常用 |
和相邻概念的区别
- 熔断(Circuit Breaker):连续失败就干脆不发了,快速失败;退避是还发,但越发放越慢。
- 降级(Graceful Degradation):降级解决"请求少了之后功能怎么办",退避只解决"什么时候再发"。
- 幂等(Idempotency):重试的前提是操作幂等。转账接口盲目重试,可能扣两次钱——这类请求要么带幂等键,要么别自动重试。
对从业者的实际意义
调别人 API 时,先翻 SDK 的重试配置:不少默认是固定间隔重试,批量任务一开几千并发,正好把对方打挂。自己提供服务时,429 响应里带上 Retry-After 头,明确告诉客户端"等 3 秒",比让它自己猜更省事。至于偶发失败到底算限流还是算真故障,看状态码和响应体,具体语义以对方官方文档为准。
对普通人来说,App 里那个转圈、"发送失败,稍后重试",背后跑的就是这套东西。
