社区食堂的午市,收银台归陈姐管。老规矩:订饭不用喊,把纸条从窗口塞进来就行,纸条上写工位号和日期。陈姐把条子钉在身后的软木板上,做一份,划一道,收一次钱。
三楼的小刘总不放心。纸条塞进去听不到回音,他就再写一张塞一次。有时一个中午,同一个"3-17,周四"能来三张。
陈姐从不恼。她只看编号:软木板上钉过"3-17,周四",后面再来的一律折起来压在盘子底下,饭照旧只做一份,钱照旧只收一次。小刘来取饭,她把那份饭递出去,顺带把折起来的纸条还给他:"收到三回,做了一回。"
新帮工小方来的第一天,赶上午市最忙。他见条子就下锅,见条子就记账。收档一算,"3-17"做了三份,账上也记了三笔。小刘拿着账单来对,脸涨得通红:他只吃了一份饭,却欠着三份的钱。
陈姐没说小方手脚不勤——他比谁都勤快。她拍了拍软木板:"咱们这行,条子会重来,人会不放心,这都拦不住。你能守住的只有一条:别数条子来了几张,先看板上钉没钉过这个号。钉过了,饭就是做过了;他再塞十张,你也只当他是在问'我那份有了吗'——那就把原来的答案,再递一遍。"
小方把多记的两笔划掉,多做的两份自己吃了。第二天上手,他的第一件事不是开火,是回头看板。
这个故事讲的是什么?
概念:幂等性(Idempotency)——Stripe 等支付 API 的核心设计之一。
幂等性指:同一个操作执行一次和执行多次,产生的效果相同。它解决的是支付里的"重试安全"问题——网络会超时,客户端会重发,用户会重复点击,但扣款绝不能重复。Stripe 的做法是让调用方为每笔请求生成一个唯一的幂等键(Idempotency-Key):服务端第一次见到这个键,正常执行并把结果存下来;之后再见到同一个键,不再重复执行扣款,只把上次存好的结果原样返回。
- 纸条 = 客户端发来的请求;小刘反复塞纸条 = 网络不稳时的重试
- "工位号 + 日期" = 幂等键(Idempotency-Key)
- 软木板 = 服务端存储的"已处理的键 + 结果"
- 只做一份、只收一次钱 = 操作只执行一次
- 把同一份饭再递一遍 = 对重复请求返回缓存的原始响应,而不是报错或忽略
- 小方"见条子就下锅" = 没有幂等保护的接口:重试直接变成重复扣款
检验问题
- 理解检验:陈姐收到重复纸条时,为什么不是置之不理,而是"把原来的答案再递一遍"?对应到支付接口:第二次收到同一个幂等键的请求,正确的行为是报错、静默忽略,还是别的什么?为什么?
- 迁移检验:消息队列的消费者经常会收到重复投递的消息。请举一个你自己业务里的例子:你会拿什么当"工位号 + 日期",把一个本来不幂等的操作改造成可以安全重试的?