BASE, Basically Available(基本可用) 、Soft-state(软状态) 和 Eventually Consistent(最终一致性),EDA (Event Driven Architecture),相信大家都背的滚瓜烂熟了。但是,到底如何实现?
下面以电商订单支付业务为例,来介绍BASE/EDA如何实现。在微服务架构中,我们一般有“订单服务”和“支付服务”两个微服务。订单的支付状态有两个:“未支付”和“已支付”。在处理支付业务的时候,需要查询订单的状态,“未支付”的订单才能支付。未支付的订单在一定时间后自动取消(将预扣减的库存还回去)。
可能问题:
- 如果用户正好在订单要自动取消前一刻开始支付,然后订单被自动取消,然后用户支付完成,然后就出现了不可思议的现象:用户支付成功了一个被取消的订单。。。
- 用户支付成功后,马上刷新页面,由于订单系统可能没有及时更新订单状态,用户可能看到订单任然是“未支付”状态。
为了防止这种情况,我们引入一个软状态:支付中。 支付中是一个中间状态,它有若干作用:
- 让支付中的订单不能超时取消,防止上面的“用户支付成功了一个被取消的订单”这个现象出现。
- 用户支付成功后,马上刷新页面,用户看到的是“支付中”,而不是“未支付”。
什么是软状态(BASE中的Soft State)?
- 软状态是在没有用户输入(操作),会自己改变的状态;
- 软状态一般是中间状态;
- 引入软状态的目的是为了控制业务流程。
如何设计支付业务?
如下图: ①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳
- ①支付服务同步调用订单服务,设置订单的状态为“支付中”
- ②做幂等检查,如果是重复调用直接返回上次调用的执行结果
- 接口参数中需要有幂等ID
- 幂等ID可以是一个不重复的随机数,例如UUID
- 记录调用历史,如果幂等ID已经使用过,说明这是第N次(N>1)调用,直接跑到⑤去,返回第一次执行的结果
- 幂等需要考虑并发的情况,多个线程同时在执行,需要考虑使用分布式锁,推荐使用数据库乐观锁。
- ③没有重复调用,检查订单状态。
- 如果不是“未支付”,返回设置失败。
- ④如果状态是“未支付”,在乐观锁(CAS,或者其它分布式锁方案)中设置状态为支付中,然后返回设置结果。 这里有可能返回失败(如果有锁竞争)。
- ②做幂等检查,如果是重复调用直接返回上次调用的执行结果
- ⑥同步得到支付中软状态设置结果,设置成功后才能进入后续支付流程。
- ⑦ 幂等判断是否重复支付
- 这里幂等ID为: 订单编号 + “_PAY”
- ⑧如果有成功支付记录, 返回成功。
- 没有成功支付记录,进入⑨支付流程
- ⑨ 进入支付这个流程,这个流程包含若干子流程,例如调用支付宝/银行等支付业务等。
- ⑩ 将支付结果返回给BFF,将“支付结果事件⑬”的发送到消息队列里去
- ⑦ 幂等判断是否重复支付
- ⑭订单微服务收到支付结果,修改订单状态。
- 如果支付成功,设置订单为“已支付”。(订单支付状态设计为单独字段:只有未支付/支付中/已支付3个状态。)
- ⑯如果支付失败,订单状态为“支付中”,⑰将订单设置为“未支付”。
- 此时订单状态有极小的概率为“未支付”或者“已支付”. 可能发生原因为消息重复,消息到达顺序错乱。这里微持原订单状态即可,不用修改。
- 数据不一致处理
- ⑪因为各种原因,有一定几率出现可能数据不一致(例如,EDA中的事件丢失),订单服务将状态为“支付中”,已经超过一定时间的订单号,发送给支付服务,要求订单服务补发支付结果⑫。
- ⑫ 支付服务重新将支付结果查询出来,补发支付结果事件
-
如果支付系统中没有订单的支付记录,补发支付失败。
-
如果订单的支付流程仍然没有完成,不做处理。(此时支付系统有严重问题。但是订单完成后会有支付结果消息给订单服务)
-
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END