---
## 12. 进阶补丁:重试、抖动、以及“别把下游当砂锅”
讲熔断不讲重试,就像讲健身不讲饮食——容易练出“看起来很努力,实际上很虚”的架构。
### 12.1 什么时候该重试?
建议用**可重试错误清单**,别靠感觉:
- 网络瞬断、连接 reset、DNS 抖动 → 可重试(次数少)
- 下游返回 429/503(明确限流)→ 先退避再重试(最好尊重 Retry-After)
- 业务校验失败(参数不合法、余额不足)→ 不要重试
- 下游超时(你已经等到预算上限)→ 通常不要同步重试,改走异步补偿
### 12.2 指数退避 + 抖动(Jitter)
没有抖动的重试,会让雪崩更整齐、更壮观。
```java
long base = 20; // ms
for (int i = 0; i < 2; i++) {
try {
return riskClient.check(req);
} catch (IOException e) {
long sleep = (long) (base * Math.pow(2, i));
long jitter = ThreadLocalRandom.current().nextLong(0, 10);
Thread.sleep(sleep + jitter);
}
}
throw new DownstreamException("risk retry failed");
```
记住:在交易主链路里,重试次数宁可少,宁可让系统快速失败,然后由异步流程把事办完。
---
## 13. 返回码与前端协作:降级不是“随便回一句”
降级后的返回,必须让客户端知道“现在发生了什么”。
一个实用的约定:
- `OK`:已成功创建订单
- `PROCESSING`:已受理,结果稍后确认(配合查询接口/通知)
- `RETRY_LATER`:系统繁忙,请稍后再试(可引导排队/限流页面)
- `FAIL`:明确失败(风控拒绝/库存不足/余额不足)
并且在响应里放一个 `traceId`,让客服别再拿“截图”逼你破案:
```json
{
"code": "PROCESSING",
"message": "系统繁忙,订单已受理,正在确认结果",
"orderId": "20260204000123",
"traceId": "0af7651916cd43dd8448eb211c80319c"
}
```
这比“HTTP 200 + message=success”成熟太多。
---
## 14. 最后一句:韧性不是写在 PPT 上,是写在‘被打爆的夜里’
你可以不喜欢‘降级’这个词,它听起来像妥协。
但交易系统的现实就是:
- 世界不总是 99.99% 的
- 下游不总是你的
- 流量不总是可预测的
所以我们要把系统训练成一种很成人的性格:
> 能拼的时候拼;拼不过的时候,先把自己活下来。
(连载未完,下一篇见。)