设计总览
- 之前我的微服务项目中设计了售票和订单两个服务,订单服务在收到订单消息后会将其放入redis中zset结构做过期处理。
- 如果用户按时支付,会更新订单信息;如果没有按时支付,会将订单从zset中取出来放入一个任务队列。
- 一个后台协程不断从任务队列获取任务进行取消订单处理,具体是删除未支付的订单,并向kafka发送退票消息给售票服务。
正文
订单过期核心逻辑
收到订单放入zset时,订单信息作为member,当前时间+过期时间(如10分钟)作为score。一个后台协程不断取出zset中score在-inf到当前时间的时间戳之间的member,这些就是没有按时支付的订单,会进行取消订单处理。
第三方支付和目前系统的整合
刚开始开发确认支付接口,我只是设计了一个虚拟的支付接口,没有直接进行真正的支付操作。现在考虑接入微信支付等接口,虽然微信支付可以支持支付过期时间,但还是有可能出现极端情况下用户卡点进行支付,此时可能受网络影响,第三方接口返回时间过长,导致zset中订单记录被错误地放到了任务队列中,执行了取消订单事件。(付了钱,不给票,用户碰到这种情况,投诉电话会被打爆的)
目前的解决方法是另辟蹊径,假如过期时间是今天11:40:30,原来的逻辑是立即将订单放入取消订单的任务队列。现在我们延迟3s或者其他的时间,再进行之前的操作。(这个时间就是考虑到网络可能产生的一些延迟)
这种思路听上去很low,但我觉得还不错,有时一些极端问题的解决可能就是一些很朴素的方法,这样不需要额外的(亦或是相对来说反逻辑的)判断和补偿操作,性能上也ok。
提一下第三方支付实现过期时间的方式
没看过人家的实现,我是猜的,但我觉得这也是一种实现方式
- 第三方接到一次支付请求,带有过期时间,那就在每一次需要判断过期的逻辑开始之前,前置检查一下本次支付是否过期,把过期时间和当前时间比对一下,没到达过期时间才允许继续执行后续动作。
- 比如我是12:30的过期时间,用户12:28把支付界面打开,这时是可以正常操作的,一直等到12:32才输入密码进行支付,此时就会判断支付过期失败。
- 如果非要整成12:30让用户调起的支付界面显示超时关闭,这个实时控制意义不大,没什么必要,毕竟支付这东西很纯粹,要保证极致的准确性、可靠性,其他都无所谓。
- 为了验证这个想法,我还专门去京东下单了一个玩具。我卡在订单过期前10s进行支付,迟迟不输入密码,眼睁睁看着订单过期,最后我输入了密码,进入了后续的流程。果然,提示我订单超时,看来他的实现也就是这样的,黑盒测试最有用的一集。
结束
关于延迟支付和调用第三方支付接口,就先聊到这里,拜拜。