数据一致性的障碍

做了微服务拆分后,还可能会出现数据不一致的问题。比如支付服务中,支付状态发生变更后要通知订单服务修改对应订单的状态。如果支付服务没有正常通知到订单服务,或者订单服务接到通知后没能正常处理通知,就会导致支付服务的支付状态和订单服务的支付状态不一致,也就是数据会不一致。

那么如何避免数据不一致的问题产生呢?

我们通常所说的服务间数据一致性,主要包括数据强一致性和最终一致性。对于强一致性,使用的业务场景很少,而且会有明显的性能问题。所以这里我们主要讨论最终一致性。

一般我们可以采用如下几种方式来保证服务间数据的最终一致:

定时任务重试,同步调用接口

这种方式,采用定时任务去扫表,每次定时任务扫描所有未成功的记录,并发起重试。注意,要保证重试操作的幂等性。

这种方式的优点是:实现简单。缺点是:需要启动专门的定时任务,定时任务存在一定的时间间隔,实时性会比较差。而且同步接口调用的方式,耦合较重,有时无法避免循环依赖的问题。

比如,Order服务可以调用Pay,Pay做为基础服务不应该调用Order。当Pay的某笔交易状态发生变更后,需要通知Order。如果采用定时任务的方式就需要Order提供一个接口,定时任务扫描过程中同步调用这个接口去更新Order的订单状态。这样又违反了单向依赖的原则,形成了循环依赖。

异步消息队列,发送事务型消息

如上图,以电商下单流程为例。下单流程最后一步,通知WMS捡货出库,是异步消息走消息队列。


  1. public void makePayment() { 
  2.    orderService.updateStatus(OrderStatus.Payed); //订单服务更新订单为已支付状态 
  3.    inventoryService.decrStock(); //库存服务扣减库存 
  4.    couponService.updateStatus(couponStatus.Used); //卡券服务更新优惠券为已使用状态       
  5.    发送MQ消息捡货出库; //发送消息通知WMS捡货出库 

按上面代码,大家不难发现问题!如果发送捡货出库消息失败,数据就会不一致!有人说我可以在代码上加上重试逻辑和回退逻辑,发消息失败就重发,多次重试失败所有操作都回退。这样一来逻辑就会特别复杂,回退失败要考虑,而且还有可能消息已经发送成功了,但是由于网络等问题发送方没得到MQ的响应。还有可能出现发送方宕机的情况。这些问题都要考虑进来!

幸好,有些消息队列帮我们解决了这些问题。比如阿里开源的RocketMQ(目前已经是Apache开源项目),4.3.0版本开始支持事务型消息(实际上早在贡献给Apache之前曾经支持过事务消息,后来被阉割了,4.3.0版本重新开始支持事务型消息)。

【声明】:芜湖站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

相关文章