API 幂等性设计:唯一约束、事务与故障恢复
先区分请求重试与业务重做
创建订单、提交支付或触发任务时,客户端超时并不代表服务端没有执行。若直接重试,同一个业务动作可能发生两次。接口幂等性解决的是重复请求产生重复副作用的问题,关键在于让相同业务意图携带同一个标识,而不是简单限制请求频率。
幂等键由调用方生成
客户端第一次提交前生成随机请求标识,放入 Idempotency-Key 请求头,并在重试时复用。一次新的业务提交才生成新键。服务端按用户、接口和幂等键共同确定记录范围,避免不同用户使用相同键时相互影响。还应保存规范化请求内容的摘要:同一键对应不同参数时返回明确的冲突错误,不能沿用旧结果。
让唯一约束负责并发竞争
以 PostgreSQL 为例,可以建立处理记录表,用唯一约束保证同一请求只有一个执行者。
CREATE TABLE api_idempotency (
user_id bigint NOT NULL,
operation text NOT NULL,
idem_key text NOT NULL,
request_hash text NOT NULL,
state text NOT NULL,
response_body jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (user_id, operation, idem_key)
);处理请求时先尝试插入记录。若已存在,读取状态和参数摘要:完成态返回原业务结果,处理中按接口约定返回状态查询地址或可重试错误。不要采用先查询、没有再插入的无锁实现;两个并发请求可能同时查不到,随后都执行业务。
业务提交必须覆盖故障窗口
如果业务数据和幂等记录位于同一个数据库,应在同一事务内更新业务并保存最终结果。这样提交成功而响应丢失时,重试仍能读到结果。若事务之外还要调用支付平台或发送消息,仅有本地记录不够:外部调用也需要稳定业务号,消息可结合事务发件箱与消费者去重。跨系统的每个副作用都需要独立考虑重复执行。
两个容易遗漏的边界
第一个边界是处理中记录长期不结束。进程在写入状态后退出,会留下悬挂任务。需要执行租约、超时核查或恢复队列,并通过业务状态确认是否已完成,不能看到超时就盲目重跑。第二个边界是记录清理过早。缓存过期后旧请求再次到达,可能被当成新请求。保留期限应覆盖真实重试窗口;订单号、支付流水等长期业务唯一约束仍应保留。
用故障场景验收
至少测试相同键并发提交、同键不同参数、事务提交后断开连接、处理中进程退出,以及过期后重复请求。检查最终业务记录数与外部副作用次数,不能只看 HTTP 状态码。幂等键、数据库唯一约束、事务和外部系统业务号需要共同工作,才能让接口在重试与故障下保持可预测的行为。
- 点赞
- 收藏
- 关注作者
评论(0)