接口幂等设计:重试安全与去重键的落地细节
一次网关超时,前端自动重试,用户却收到了两笔订单。排查下来,下单接口本身没有幂等保护:第一次请求其实已经写库成功,只是响应在回程丢了。这类问题在微服务和移动端弱网环境里非常常见。
超时不等于失败
调用方看到 timeout,只能确定“没等到响应”,不能确定“服务端没执行”。因此任何可能被重试的写接口,都必须假设自己会被调用多次。重试策略越积极,幂等设计就越不能省。
幂等键放在哪一层
推荐由客户端生成一个 request_id(UUID 或“业务单号+动作”),随请求头传入;服务端在同一个事务里写业务表和去重表,去重表对 (tenant_id, request_id) 建唯一索引。重复请求撞到唯一键时,不再执行业务,而是返回第一次的结果。
不要在网关层用 URL+body 做哈希:字段顺序、时间戳、灰度参数都会让同一个业务请求算出不同指纹;也不要让服务端自己生成键,那样重试时根本对不上。
处理中的请求怎么返回
如果第一次请求还在执行,第二次请求会看到“记录已存在但状态是处理中”。此时应返回 409 或 202,并带上查询进度的地址,而不是傻等或直接报错。等到第一次完成,再把结果补进去重记录,后续重试就能拿到稳定响应。
去重记录保留多久
至少覆盖“客户端最大重试窗口 + 人工补偿窗口”。按天分表、保留 7~30 天通常够用;金融类场景按审计要求延长。清理任务要和业务写入解耦,避免大表删除拖慢主库。
别把幂等做成全局锁
用 Redis 的 SETNX 做短期防重可以,但只能当第一道闸,最终一致性仍要落到数据库唯一约束。锁过期时间设得太短,长事务会漏;设得太长,失败请求会长期占坑。数据库唯一键加状态机才是兜底。
一份检查清单
- 所有写接口是否声明了幂等键来源;
- 唯一索引是否包含租户维度,避免跨租户碰撞;
- 重复请求返回的是首次结果还是模糊报错;
- 处理中状态是否有超时回收;
- 去重记录过期后,重试会不会重新执行。
幂等不是给接口加一个“防重复”开关,而是把“可能重复”当成常态来设计。把键、状态和过期策略三件事定清楚,重试才真正安全。
- 点赞
- 收藏
- 关注作者
评论(0)