先看现象。订单服务调内部推理接口生成摘要,幂等键用请求体哈希再拼秒级时间戳生成,网关超时重试时请求体一样但时间戳变了,下游把它当成新请求,同一笔订单生成了两条摘要记录,对账时才发现金额重复。

排查方向其实有限,先把可能性排一排。幂等键里拼了时间戳,重试时算出来的值必然不同。也可能是重试逻辑在客户端重新构造请求,没有复用原来的键。

另外,幂等键只放在请求体,网关重排 JSON 后哈希也变了。

少数情况下是下游只按幂等键判重,没有业务唯一索引兜底。

动手时先确认两次请求带的幂等键是否相同

python -c "import json;[print(json.loads(l)['key']) for l in open('retry.log')]"

接着改成按业务单号生成稳定键并放到请求头

python -c "import hashlib;print(hashlib.sha256(b'ORDER-20261113-8842').hexdigest())"

下游补一条业务唯一索引兜底

psql -c "CREATE UNIQUE INDEX CONCURRENTLY uq_digest ON digest(order_no, scene);"

末了用同一单号重放两次确认只落一条

for ($i=1; $i -le 2; $i++) { curl -s -X POST http://api:8080/digest -H "Idempotency-Key: ORDER-20261113-8842" -d @body.json }

恢复的判定其实很直接。两次重放返回同一个记录 id,摘要表里该单号只有一行,网关超时后重试不再产生新记录。

幂等键的取值只能由业务输入决定,时间、随机数、自增序号混进去都会让重试变成新请求。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。

作者 9823189