截拳道,指的是不拘于形式,思想上成熟的觉悟,以水为本质而攻击,反击;将一切化解于无形。截拳道的最大特点,是注重于“生活上实际的运用”,抛弃了传统武术复杂的形式套路。

前文回顾

本文是紧接着上一篇​​分布式事务之基于消息(一)最大努力通知​​。在上一篇文章中提到所谓的“最大努力通知”其实本质就是通过一种重试机制去调用下游的服务,“尽最大努力”去让下游服务完成整个分布式事务。而谈到异步解耦+重试一般就会与消息中间件结合起来了。

那么就又涉及到了一个问题,下游服务执行的可以通过重试去“尽最大努力”通知,但是上游的“给消息中间件发消息”这个操作与本地事务(或者说当前分布式事务中的其他子事务)怎么保持一致性,需要保证本地事务执行成功了,消息就肯定能发送出去了,换句话说,怎么保证消息的发送方和消息的接收方数据是一致的。

在​​分布式事务之基于消息(一)最大努力通知​​中也分析了,发送消息这个操作无论是否与本地事务放在一个事务中,都很难完美的解决消息的发送方和消息的接收方数据一致。

于是又引入了另外一种思路:本地消息表。就是我们可以将要发送的消息先存储在本地 DB 中,然后通过定时任务去发送消息。当然这种方案也有缺点,就是感觉有点杂,通用性不太好。

正文

本文主要就是介绍另外一种解决消息发送一致性问题的方案:事务消息,当然称之为可靠消息也行。

一般解决方案的整体流程图如下(朋友说能看得清,请宽容忽略字丑):

分布式事务之基于消息(二)可靠消息_分布式

其实这里最核心的一个流程是从 A(即事务发起方)到可靠消息服务。这里运用了一个“半消息”的机制去确保消息的发送方和接收方的数据一致,有 2 个关键点:

  1. 先发半消息,再执行本地事务
  2. 回查机制

为什么事务发起方要先发半消息,再执行本地事务呢?如果先执行本地事务并且提交了,结果服务宕机了,这时候消息没有发出去,出现了不一致。如果发消息和本地事务包含在一个本地事务中,在​​分布式事务之基于消息(一)最大努力通知​​中也分析了,这里就不赘述了。

再结合第二点回查机制,即使半消息发送后本地事务执行失败或者压根还没执行服务就挂了,这时候可靠消息服务还有一个回查机制,它会去请求服务发起方,看这个半消息到底该怎么办,如果服务发起方本地事务执行成功了,那么半消息状态就会变成“已确认”,并且可靠消息服务会向 MQ 发送消息,进而通知下游服务。

其实这就是可靠消息,或者说事务消息,整体流程与 TCC 相比简单很多。

然后有的 MQ,如 Rocket MQ、Rabbit MQ,它们已经支持事务消息了,也就是说它们可以直接替代上图中的可靠消息服务+MQ 的功能。但是其实一般不建议去重度使用 MQ,这样在出现一些问题或者需要替换 MQ 的时候会非常复杂。

在上一篇​​分布式事务之基于消息(一)最大努力通知​​中也有朋友参与讨论,有 MQ 事务消息,就必须要使用本地消息了,因为他们都可以保证消息的可靠。但是我个人觉得本地消息相比可靠消息会减少 RPC 的调用次数(不需要先 RPC 发半消息,事务执行完成再 RPC 提交半消息),可能性能会更优;而且在实现可靠消息的时候,需要有一个可靠消息服务或者支持事务消息的 MQ,但是本地消息表就没有这么多要求。总得来说它们各有优劣,没必要过于纠结,我们更应该专注于“分析“并”合理解决”当前系统遇到的分布式事务问题,而不是注重于“形式”。

References

欢迎关注公众号:

分布式事务之基于消息(二)可靠消息_RPC_02