下单接口把幂等键写在 body 的 request_id 字段,前面挂了一层网关做参数规范化,键顺序被重排、空字段被删掉,服务端按整个 body 算哈希,重试请求算出的值不同,同一订单落了两条。

一开始怀疑是「幂等键放在业务字段里,容易被上游改写」,查了一圈,方向不对。

把日志和配置对完,能解释这个现象的基本就这几处。幂等键参与哈希的输入包含了整个请求体,字段顺序变化就失效。也可能是网关会补默认值和删空值,两次请求体不完全一致。

不容易想到的是,服务端用 body 哈希而不是显式的键值做去重。

上来先对比两次请求体的哈希

python -c "import hashlib,json;a=json.dumps({'sku':'A1','qty':1},sort_keys=True);b=json.dumps({'qty':1,'sku':'A1'},sort_keys=True);print(hashlib.md5(a.encode()).hexdigest()==hashlib.md5(b.encode()).hexdigest())"

随后把幂等键挪到请求头并在网关透传

curl -s https://api.example.com/v1/orders -H 'Idempotency-Key: ord-20261111-8821' -H 'Content-Type: application/json' -d '{"sku":"A1","qty":1}' -o /dev/null -w '%{http_code}\n'

服务端只按这个头做唯一约束

python -c "print('unique index on idempotency_key')"

末了重放同一个键验证只产生一条记录

curl -s https://api.example.com/v1/orders -H 'Idempotency-Key: ord-20261111-8821' -d '{"sku":"A1","qty":1}' | jq '.order_id'

别只看报错消失,验证要多做一步。同一个幂等键重复提交只生成一条订单,第二次返回第一次的结果,body 字段顺序变化不再影响判定。

幂等键要独立于业务报文存在,放请求头或单独字段,用整个 body 算哈希在有任何网关规范化的链路里都不可靠。这类问题最好一次改到位,反复小改反而容易把原因搅混。

作者 9823189