内容简介:首先了解强一致性和弱一致性。在微服务中,系统间通过数据一致性还有个重要的前提:支持幂等。也就是说,只要请求参数不变,那么无论重复请求多少次,结果都一样。在对接第三方支付时,这个词出现的频率还是老高的。
MySQL
的事务是数据一致性的典范,事务内的执行要么都成功,要么都失败。但业务系统涉及系统间的相互调用,涉及的数据库也不尽相同,所以实现数据一致性还是有挑战的。
首先了解强一致性和弱一致性。在微服务中,系统间通过 HTTP
的方式相互调用,很难实现数据的强一致。我们这里主要说弱一致性,也就是数据最终一致性。
数据一致性还有个重要的前提:支持幂等。也就是说,只要请求参数不变,那么无论重复请求多少次,结果都一样。在对接第三方支付时,这个词出现的频率还是老高的。
购买业务
蜗牛要在一家电商网站买电子书,整个购买流程和涉及的系统 虚构 如下图。过程涉及检查它是否已经买过,然后是生成订单号、支付、交付(实际上订单系统不包含支付功能,这里简化处理)。
<center>
</center>
交付涉及三个系统,在任何一个系统内,数据库的事务都只能保证它服务内的数据一致。而且,如果在事务过程中引入了调用第三方的 HTTP
请求,数据库的事务执行结果甚至有可能会被污染。比如, HTTP
请求超时返回失败,但实际上请求却执行成功的场景。
代码设计
参考之前写的 Saga Pattern
模式,对任何一个外部服务的调用都引入两个行为: 执行
和 补偿
。补偿是对执行结果的修正。比如对于用户支付失败的场景,补偿行为可以是接口重试、可以是直接退款、还可以推送 MQ
异步修复等。
统一使用 interface
来定义一套规范。每一种支付方式以及购买产品所调用的外部服务可能不尽相同,用 interface
来达到统一调用的目的。补偿的行为都基于执行动作返回的错误,所以我们需要实现自己的错误码。
type DeliverPattern interface { //是否需要执行交付流程 Check(ctx *context.Context) (bool, error) //支付及支付补偿 DoPay(ctx *context.Context) error PayCompensate(ctx *context.Context, doErr error) error //交付及对应的补偿 DoDeliver(ctx *context.Context) DeliverCompensate(ctx *context.Context, doErr error) error }
如何补偿
对于如何补偿,不同的业务有不同的补偿方式,当让不能一概而论。但整体的思想,我觉得还是不外乎两种。当然,下面的两种描述是自己这样称呼的。
事务类
首先便是数据库 事务
类,任何一个流程失败,整个事务内的操作全部反向回滚。沿着这样的思路,接口定义中 PayCompensate
应该实现 DoPay
的回滚操作,而 DeliverCompensate
应该实现 DoPay
以及 DoDeliver
的回滚操作。
我们需要在操作的同时维护一个回滚操作的队列,任何一个 Do
行为的完成,都需要在回滚队列中插入对应的回滚方法。当后面任何一个 Do
操作失败,统一执行回滚队列的方法。
这样的困境在于你不能完全保证回滚方法一定成功执行。而且出于性能考虑,还需要结合异步队列,通过后台重试来保证整个业务流程彻底回滚成功或回滚失败。
状态类
每个业务都会拆分成各个更小的块,就跟写代码空行一样,这里的 DeliverPattern
也是根据业务流程拆分成更小的执行粒度。我们可以为每个 Do
行为都设置一个状态码,类似于状态机,记录每一次购买的各个状态。
const ( StatusDoPaySuccess = 1 StatusDoPayCompensateSuccess = 2 StatusDoPayCompensateFailure = 3 )
这样我们补偿方法中执行的不再是回滚操作,而是 Do
方法的重试。如果补偿成功,继续执行后续的操作,如果补偿失败,记录下该状态,后续看看怎么补偿。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
未来世界的幸存者
阮一峰 / 人民邮电出版社 / 2018-6-1 / 39.00 元
本书为阮一峰博客文集,主要收录的是作者对技术变革的影响的一些思考,希望能够藉此书让读者意识到世界正在剧烈变化,洪水就在不远处,从而早早准备出路。本书适合所有乐于思考的读者。一起来看看 《未来世界的幸存者》 这本书的介绍吧!