也不能!
因为车主在那等着呢!难道手机上一直在那转圈 , 跟车主说现在负载高 , 请先喝杯茶 , 让子弹飞一会?
因为必须要立即告知用户处理结果 , 所以这种情况下(扣款超时且未查到扣款记录)只能告诉用户扣款失败 。
只不过 , 在告知用户之前 , 业务系统需要先撤销本次扣款申请 , 告诉储值卡系统本次扣款流程不能执行了(回滚本次事务) 。
于是小林做了如下优化:
文章插图
现在系统健壮多了 , 很久没出现上次的问题了 , 小林又跑去撸猫了 。
某天深夜 , 小林又接到运营同学电话:上次的问题重现了!
尼玛 , 见鬼了!
小林又跑去查日志 , 发现确实是扣款接口超时了 , 但撤销接口调成功了(虽然调了几次才成功)——那为毛还扣了钱啊?
想了半天 , 小林终于发现了问题:和前面提到的查询问题一样 , 撤销的时候同样无法保证那个该死的扣款流程已经跑完了啊!这次是因为撤销逻辑确实执行了 , 但执行的时候扣款逻辑还在跑(还没写库)!
所以撤销接口必须考虑两种情况:
- 撤销的时候 , 扣款已经发生了——此时能正确撤销;
- 撤销的时候 , 扣款还没发生 , 但扣款流程正在执行——此时撤销会失败 , 稍后钱仍然会被扣掉;
小林的撤销逻辑是这样的:
文章插图
原本由业务系统同步调撤销接口 , 现在改成走调度系统异步撤销 , 业务系统投递撤销任务完成后立马返回结果给客户端 。
因为有异步重试机制 , 撤销总是能成功(除了实际中几乎不会发生的极端情况) , 因而这次一定能保证不会意外扣钱!
小林同学抱着如释重负的心态继续撸猫 。
然而 , 安稳日子没过几天 , 一个雷电交加的夜晚 , 手机再次响起:车主储值卡消费的钱莫名其妙给人家退回去了!油站打电话要我们赔偿!
小林赶紧查日志 , 发现场景是这样的:车主王某用储值卡支付 1000 元油款 , 失败了;十几秒后车主再次用储值卡发起支付 , 成功了 。
支付最终成功了 , 莫非人工退钱了?没看到任何退款记录啊?
抓耳挠腮 , 百思不得其解 。小林只能打电话给储值卡系统负责人小李 。
小李一顿查日志 , 最终发现这笔钱是被调度系统调撤销接口给撤销了!
小林如梦方醒 , 才知道之前自己自鸣得意地犯了个天大的错误 。
本次消费 , 业务系统共向储值卡系统发起了两次扣款申请——虽然都是同一笔订单的扣款 , 却是两个独立的事务 。
小林(以及储值卡系统)的错误在于 , 撤销操作是作用在订单上 , 而不是事务上 。
在本次事故中 , 第一次扣款超时后 , 业务系统投递了撤销任务;而后车主又对该笔订单(订单号相同)发起了第二次扣款 , 成功了;与此同时 , 调度系统第一次撤销失败(卡系统未找到消费记录 , 或者接口超时) , 一段时间后又发起第二次撤销——而这个时候 , 车主已经完成了第二次扣款且成功了 , 于是这次的撤销便作用在这个成功的扣款上(储值卡系统的扣款和撤销接口都是根据订单号来的 , 它能保证同一笔订单不会重复扣款 , 但撤销的时候无法区分扣款是哪次发起的) 。
我们画下流程:
文章插图
如图 , 第二次的扣款被调度系统撤销了 。
小林和小李这才发现需要给扣款和撤销接口增加事务编号 。
之前扣款接口主要参数是 card_no、order_code、amount , 现在变成 card_no、order_code、trans_id、amount 。
推荐阅读
- 窗帘为什么不能用蓝色?
- 网络编程之网络丢包故障如何定位?如何解决?
- 拖放式Glade界面设计与 FreeBASIC编程 + LinuxMint
- 签证为什么要存款证明?
- 为什么说番鸭有毒?
- 泡红茶时为什么起沫,安息乌龙茶的什么是白瓷
- |为什么生完孩子后,妻子宁可带孩子也不想出去上班?
- |为什么不在工作场所随便道歉?
- 为什么国外歌手不在中国开演唱会?
- 红茶走水不充分,红茶为什么泡不开