可靠消息最终一致性解决方案

可靠消息最终一致性方案是指当事务发起方执行完成本地事务后发出一条消息到消息中间件,事务参与方 (消息消费者) 一定能够接收到消息并处理事务成功。此方案强调的是只要消息发给事务参与方,则最终事务一定要达到一致。同时作为对分布式事务的完善、补充,本文对于最大努力通知这一方案的基本思路原理也将会进行介绍

可靠消息最终一致性:基于本地消息表

本地消息表,作为可靠消息最终一致性的一种典型实现方案。最初由 eBay 提出,其亦是对 BASE 理论的体现、实践。其基本原理、思路很简单。这里以订单服务、库存服务为例展开说明。当客户下单后,需要先通过订单服务在订单表中插入一条订单记录,再通过库存服务实现对库存表中库存记录的扣减。但这里即会存在一个问题,由于订单表、库存表分别位于订单服务、库存服务的数据库。传统的本地事务显然无法解决这种跨服务、跨数据库的场景。而基于本地消息表的分布式事务方案则可以在对业务改动尽可能小的前提下保障数据的最终一致性

具体地,在事务发起方即这里订单服务的数据库中再增加一张本地消息表。向订单表中插入订单记录的同时,在本地消息表中也插入一条表示订单创建成功的记录。由于此时订单表、本地消息表位于同一数据库当中,可以直接通过一个本地事务来保证对这两张表操作的原子性

与此同时,在订单服务中添加一个定时任务,不停轮询、处理本地消息表。具体地,将消息表中未被成功处理的记录通过 MQ 投递至库存服务。而库存服务在从 MQ 中接收到订单创建成功的消息后,对库存表进行库存扣减操作。在库存服务完成扣减后,通过某种方式告诉订单服务该条消息已经被成功消费、处理。这样订单服务即可将本地消息表中相关记录标记为成功处理的状态,以避免定时任务重复投递。这里库存服务确认消息消费成功的实现方式,可以直接通过 MQ 的 Ack 消息确认机制实现,也可以让库存服务再向订单服务发送一个处理完毕的消息来完成。整个方案的示意图如下所示

img

可以看到,基于本地消息表的可靠消息最终一致性方案非常简单。但在具体业务实践过程还是有一些需要的地方:

  1. 库存服务的库存扣减需要保证幂等性,一方面由于 MQ 存在自动重试机制,另一方面,当订单服务未收到库存服务对本次消息的消费确认时,则可能会导致定时任务下一次继续投递该消息至库存服务
  2. 根据实际业务需要,本地消息表中记录还应该设置一个合理的最大处理等待时间,以及时发现长时间无法得到有效处理的本地消息记录

可靠消息最终一致性:基于 RocketMQ 的事务消息

通过基于本地消息表的可靠消息最终一致性方案可以看出,其本质上是通过引入本地消息表来保证本地事务与发送消息的原子性。那如果说 MQ 本身能够直接保证消息发送与本地事务的原子性岂不是更方便了,为此在 RocketMQ 中提供了所谓的事务消息。这里我们来介绍下其基本机制,流程图如下所示

img

事务发起方首先会将事务消息发送到 RocketMQ 当中,但此时该条消息并不会对消费者可见,即所谓的半消息。当 RocketMQ 确定消息已经发送成功后,事务发起方即会开始执行本地事务。同时根据本地事务的执行结果,告知给 RocketMQ 相应的状态信息——commit、rollback。具体地,当 RocketMQ 得到 commit 状态,则会将之前的事务消息转为对消费者可见、并开始投递;当 RocketMQ 得到 rollback 状态,则会相应的删除之前的事务消息,保证了本地事务回滚的同时消息也不会投递到消费者侧,保障了二者的原子性。进一步地,如果 RocketMQ 未收到本地事务的执行状态时,则会通过事务回查机制定时检查本地事务的状态

最大努力通知

最大努力通知,亦被称作为 Best-Effort Delivery 最大努力交付,其同样对是柔性事务思想的实践。常用于对调用结果进行异步通知的业务中,特别是与第三方系统进行对接的过程中。这里,我们以电商平台通过第三方银行系统完成支付为例展开说明介绍,示意图如下所示

img

首先我们电商平台的订单服务会通过调用第三方银行系统的支付服务完成订单款项的支付,由于这里支付结果是异步获取的。所以需要等待银行系统在完成相关支付业务后,通过回调接口来通知我们系统的支付结果。但很多时候由于存在网络异常、回调接口发生异常等意外因素,第三方为了尽最大努力进行结果通知,往往会将相关结果通过 MQ 投递到通知服务,以便单独进行重复、多次的结果通知

但如果我们从第三方系统的角度考虑,如果调用回调接口一直失败,总不能一直这么重试下去啊。所以在最大努力通知的方案中,不仅需要通知的发起方 (即这里的第三方银行系统) 提供结果通知的重试机制,还需要给通知的接受方(即这里的电商平台)提供一个用于主动进行结果查询的接口。这样即使当银行系统的通知次数达到阈值,不再调用回调接口进行结果通知时,我方服务也可以在之后通过银行系统的查询接口获取相应结果

最大努力通知方案要求主要包含以下两个方面:

1)有一定的消息重复通知机制:接收通知方有可能无法接收到通知,一定要具有重复通知的机制。

2)消息校对机制:在重复通知仍然没有通知到对方,可由接收通知方主动查询信息。

与可靠消息最终一致性的区别

1)解决方案思想不同

可靠消息最终一致性:发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证。

最大努力通知:发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。

2)业务场景不同

可靠消息一致性:关注的是交易过程的事务一致,以异步的方式完成交易。

最大努力通知:关注的是交易后的通知事务,即将交易结果可靠的通知出去。

3)技术解决方向不同

可靠消息一致性:要解决消息从发出到接收的一致性,即消息发出并且被接收到。

最大努力通知:无法保证消息从发出到接收的一致性,只提供消息接收的可靠性机制。可靠机制是,最大努力的将消息通知给接收方,当消息无法被接收方接收时,由接收方主动查询消息(业务处理结果)。


参考文献

  1. 凤凰架构 周志明著
使用 Hugo 构建
主题 StackJimmy 设计