核心是禁用nginx对post/put/delete等非幂等请求的自动重试,通过清理proxy_next_upstream中的non_idempotent配置、显式限定重试条件为error/timeout/502-504、隔离写接口路径,并辅以后端幂等键校验与数据库唯一索引双重防护。

核心是让 Nginx 别对非幂等请求(比如 POST 创建订单、PUT 修改状态、DELETE 删除记录)自动重试——它一重试,后端就可能执行两次,数据就重复了。
关掉 non_idempotent 重试开关
Nginx 默认不会对 POST/PUT/DELETE 做重试,但一旦配置了 proxy_next_upstream non_idempotent,或者用了老版本(如 1.8.x 及更早),就会悄悄开启非幂等方法的重试。必须彻底清理:
- 检查所有
upstream和location块,删掉non_idempotent这个参数 - 显式写死安全的重试条件,例如:
proxy_next_upstream error timeout http_502 http_503 http_504;
注意:不包含http_500和任意http_4xx - 确认没启用
proxy_next_upstream_tries或设为 1,避免隐式多试
按 HTTP 方法分治重试策略
GET/HEAD 天然幂等,可保留有限重试;但写操作必须“零容忍”重试:
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- GET / HEAD 请求:允许重试,但建议加限制:
proxy_next_upstream_tries 2;proxy_next_upstream_timeout 6s; - POST / PUT / DELETE 接口:Nginx 层禁止重试,哪怕返回 503 —— 后端该自己扛住,或由客户端/SDK 控制重试逻辑
- 所有写接口统一走明确语义路径,比如
POST /api/order创建订单,不混用 GET 携带写参数
后端兜底:幂等键 + 唯一约束双保险
Nginx 层拦截只是第一道防线,后端必须独立校验,不能依赖 Nginx 不重试:
- 要求客户端在请求头传
Idempotency-Key: uuid-v4,服务端用该值作为业务唯一标识 - 数据库对应表加唯一索引,例如:
ALTER TABLE `order` ADD UNIQUE KEY `uk_idempotency_key` (`idempotency_key`); - 插入前先尝试
INSERT IGNORE或INSERT ... ON DUPLICATE KEY UPDATE,失败时查出已有记录直接返回
验证与观测要点
光改配置不够,得确认它真没重试:
- 在 Nginx access log 中加
$upstream_addr和$upstream_status,观察同一请求是否打到多个后端地址、或出现多次 502/503 后又成功 - 后端日志打上请求 ID 和
Idempotency-Key,排查重复 Key 是否被多次处理 - 用 curl 模拟超时或主动 kill 后端实例,验证 POST 请求是否只到达一个节点










