HTTP 请求重试为什么要考虑幂等性?哪些请求可以安全重试?
简化版
HTTP 请求重试必须考虑幂等性,因为网络超时不代表服务端一定没处理。
如果客户端对非幂等请求盲目重试,例如重复创建订单、重复扣款,就可能造成业务副作用。
通常 GET、HEAD、PUT、DELETE 从语义上更适合重试;POST 默认不幂等,除非业务引入幂等键、唯一约束、请求去重等机制。
详细版
网络调用失败有很多状态:
请求没发出去
请求发出但服务端没收到
服务端收到并处理了,但响应丢了
服务端处理中超时
客户端看到的可能都是 timeout。
如果它直接重试 POST /orders,可能创建 2 个订单。
所以重试策略要区分请求语义:
- 查询类请求通常可重试。
- 覆盖式更新通常可重试。
- 创建、扣款、发券等操作必须设计幂等。
常见方案是给请求带 Idempotency-Key,服务端用这个 key 识别重复请求并返回同一结果。
完整版教学
1. 先说明为什么超时最危险
调用方看到 timeout 时,最麻烦的是不知道服务端到底做了什么。
可能出现 4 种情况:
1. 请求根本没发出
2. 请求到达代理但没到应用
3. 应用处理成功但响应丢失
4. 应用仍在处理,客户端先超时
这些在客户端都可能表现为失败。
重试的难点不是重新发送请求,而是不确定第一次请求是否已经产生副作用。
2. HTTP 方法语义和重试关系
HTTP 规范里有安全性和幂等性概念。
安全方法不应改变服务端资源。
幂等方法表示执行 1 次和执行多次的最终效果相同。
| 方法 | 通常是否安全 | 通常是否幂等 | 重试建议 |
|---|---|---|---|
| GET | 是 | 是 | 可重试,但注意缓存和限流 |
| HEAD | 是 | 是 | 可重试 |
| PUT | 否 | 是 | 通常可重试 |
| DELETE | 否 | 是 | 通常可重试,但看业务实现 |
| POST | 否 | 否 | 默认谨慎 |
| PATCH | 否 | 不一定 | 视补丁语义决定 |
注意这是 HTTP 语义层的约定,业务实现仍然可能破坏它。
3. POST 为什么默认不能盲目重试
POST /orders 常表示创建订单。
如果第一次请求已经创建成功,但响应在网络中丢失,客户端重试后可能再次创建。
伪代码问题如下:
client -> POST /orders -> server 创建订单成功
server -> response 丢失
client timeout
client retry POST /orders
server 再次创建订单
这会导致重复订单、重复支付、重复发券、重复发送短信。
所以 POST 需要业务幂等设计。
4. Idempotency-Key 是常见方案
客户端为一次业务操作生成唯一 key:
POST /payments HTTP/1.1
Idempotency-Key: pay-20260802-0001
Content-Type: application/json
服务端处理逻辑:
- 第一次看到 key,执行业务并记录结果。
- 再次看到同一个 key,不重复执行业务。
- 返回第一次处理结果或可识别的幂等响应。
这个 key 应该和用户、接口、业务参数绑定,不能全局随便复用。
5. 服务端幂等的常见实现
常见实现方式有:
- 唯一索引,例如订单号唯一。
- 幂等表,记录请求 key 和处理状态。
- 分布式锁,避免并发重复处理。
- 状态机校验,例如只有待支付才能扣款。
- 去重缓存,短时间内过滤重复请求。
- 消息消费端用业务唯一键去重。
强业务场景通常会组合多种手段。
单靠前端禁用按钮不是幂等。
6. 重试策略还要考虑退避和状态码
即使请求可重试,也不能无限重试。
工程上通常会做:
- 最大重试次数,例如 2 到 3 次。
- 指数退避,避免雪崩。
- 只对网络错误、连接重置、
502、503、504等临时错误重试。 - 对
400、401、403这类确定性错误不重试。 - 加请求超时和总截止时间。
重试是可靠性工具,也可能放大流量。
7. 常见误区与追问
- 误区:HTTP 超时说明服务端没有执行。 超时只能说明客户端没及时拿到结果,服务端可能已经处理成功。
- 误区:POST 永远不能重试。 POST 可以在业务幂等设计后安全重试。
- 误区:GET 一定没有副作用。 规范语义上应安全,但实际系统可能有埋点、计数等副作用。
- 追问:Idempotency-Key 应该存多久? 要覆盖客户端可能重试的时间窗口,支付等场景通常要更长。
- 追问:DELETE 重试为什么通常可接受? 删除同一资源多次的最终状态通常都是资源不存在。
- 追问:重试为什么可能造成雪崩? 大量客户端同时重试会放大后端压力。
8. 加强记忆
重试题的核心链路是:失败不等于未执行,重试必须看幂等,非幂等操作要有业务去重。
面试里把“方法语义 + 业务幂等 + 退避策略”讲全,就很扎实。