前言
在面过的几家大厂中,几乎每轮的面试官(没写错,几乎是每轮面试官)都问了同样一个问题:你们的系统是分布式的系统吗?
答:是。
面试官:那么你们分布式的系统是如何解决分布式事务这个问题的呢?也就是如何保证数据的一致性。
答:我们的系统中通过 RocketMQ 的事务消息来保证数据的最终一致性。
面试官:那你说说它是如何来保证数据的最终一致性的?
答:分两部分来回答,第一部分先回答事务消息的实现流程,第二部分解释为什么它能保证数据的最终一致性。
事务消息的实现流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EgdTVLoe-1615016825036)(https://imgkr2.cn-bj.ufileos.com/49aa1164-a748-4f23-8d15-a2548742eeed.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=T%252FW9CCUIZD1GP7MASzyEHwPMh0c%253D&Expires=1614875815)]
-
首先服务 A 发送一个半事务消息(也称 half 消息)至 MQ 中。为什么要先发送一个 half 消息呢?这是为了保证服务 A 和 MQ 之间的通信正常,如果无法正常通信,则服务 A 可以直接返回一个异常,也就不用处理后面的逻辑的了。
-
如果 half 消息发送成功,MQ 收到这个 half 消息后,会返回一个 success 响应给服务 A。
-
服务 A 接收到 MQ 返回的 success 响应后,开始处理本地的业务逻辑,并提交本地事务。
-
如果服务 A 本地事务提交成功,则会向 MQ 中发送 commit,表示将 half 消息提交,MQ 就会执行第 5 步操作;如果服务 A 本地事务提交失败,则直接回滚本地事务,并向 MQ 中发送 rollback,表示将之前的 half 消息进行回滚,MQ 接收到 rollback 消息后,就会将 half 消息删除。
-
如果 commit,则将 half 消息写入到磁盘。
-
如果 MQ 长时间没有接收到 commit 或者 rollback 消息,例如:服务 A 在处理本地业务时宕机了,或者发送的 commit、rollback 因为在弱网环境,数据丢失了。那么 MQ 就会在一定时间后尝试调用服务 A 提供的一个接口,通过这个接口来判断 half 消息的状态。所以服务 A 提供的接口,需要实现的业务逻辑是:通过数据库中对应数据的状态来判断,之前的 half 消息对应的业务是否执行成功。如果 MQ 从这个接口中得知 half 消息执行成功了,那么 MQ 就会将 half 消息持久化到本地磁盘,如果得知没有执行成功,那么就会将 half 消息删除。
-
服务 B 从 MQ 中消费到对应的消息。
-
服务 B 处理本地业务逻辑,然后提交本地事务。
如何保证数据的最终一致性
实现流程说完了,可能你现在有各种各样的疑惑?
Q: half 消息是个啥?
A: 它和我们正常发送的普通消息是一样的,都是存储在 MQ 中,唯一不同的是 half 在 MQ 中不会立马被消费者消费到,除非这个 half