说是分布式事务,其实已经完全不是关系型数据库的那种强一致性事务了。只能说最终数据一致性的解决方案。
方案1, 独立消息 :
解释:独立消息,如上图,主动服务想发送一个消息给被动消费者。我们要保证消息不丢失。确保 主动 服务 和 被动消费者数据一致性。
主动服务发起一个预消息给 独立的消息服务器,但是独立消息服务器不是马上把消息传递给 消费者。而是持久化到独立消息服务器的 硬盘( 数据库 或者别的方式 都可以),只有等这个预消息被确认的时候才会投递给消费方。
这时候主动服务的发送方法 继续下面的 关联逻辑。如果报错,这个发送方法 回滚。如果正确执行,那么在提交事务之前 向 独立消息服务器 确认前面发送的 预消息
(备注:要保证这个确认不会影响到事务的提交,确认方法不能抛出任何异常,但是也可以按需求回滚事务)。并且确认预消息那一步可不不执行。独立消息服务 收到 确认以后 传递消息给 消费者。
假如主动服务事务回滚,那么这个方法的记录都被回滚,预消息没有被确认。独立消息服务器的 确认预消息机制 会在稍后 向主动服务发起询问。如果主动服务的数据回滚了,那么查询不到任何记录。这条预消息被丢弃。
如果主动事务完全了。但是确认请求忘了发,或者没有发出去,独立消息服务有主动询问机制会询问主动服务 怎么处理这个消息。
上面已经保证了主动服务的消息到 独立消息服务 绝对不丢。后面是就是保证独立消息服务到 消费者的绝对不丢。
备注:上图的 mq 不是 必须的。我后面你的实现代码用了 mq 。所以把它 画图上了。
消息确认需要发出一后。独立消息服务向 消费者 发送消息。 持久的在硬盘中的这个条消息状态改变,变成发送中。消费者收到消息处理完逻辑一后 。告诉独立消息服务,正常接收,独立消息服务 吧这条消息标记为完成。
如果消费者不告知独立消息服务完成消息,那么 独立消息服务的重发机制会再次向消费者发送这条消息。直到消费者正常确认收到这条消息。因为有重发,所以消费者可能重复处理这条消息,所以需要消费者 幂等 的处理这个消息的后续逻辑。
明显上面的过程数据传递可靠性极高。理论上100% 的不丢消息,但是明显的依赖严重,虽然独立消息服务 是一个独立运行,自带询问,自带重发的 ,和主动服务 被动消费者 无关的独立程序。但是 依旧需要主动 服务实现大量的询问接口 。(被动消费者的确认接口只有一个不是问题),
独立消息类型的代码实现:https://github.com/hualiuwuxin/reliable-message.git
方案2:最大努力通知型:
解释:支付回调就是这种模式。上面图中 mq 也不是必须的。但是一般会用。如果仔细看的朋友会发现,这个就是 独立消息服务 消息确认发送 后 到 消费者的流程的一个变种。
主动方吧消息告诉通知服务(这个过程可以通过 独立消息,可以通过事务存储等方法保证一致性),现在通知服务 已经拿到的 这个消息。并且存储了。通知服务发送消息给
被通知方,如果被通知方正确的返回,那么这个消息标记为完成。如果这个消息发送失败,那么这个消息上面标记发从次数+1,并且过一会再发送,直到被通知正确的返回,
或者发送次数超过限制。尽力的通知 对方了,但是实在通知不到就不通知了,但是提供查询接口,在对方想要直到结果的时候能够查询到这个消息。
最大努力通知型适合 跨公司之间的交互。但是需要被动主动查询才能保证数据100% 的一致性。
方案3:本地消息型:
解释:这个本地消息就简单很多了,它是独立消息的一个变种。消息存在本地数据库中,简化了消息发送的询问过程。但是 主动服务 和 消费服务之间的关联更加密切。并且不再通用。但是实现变得简单了。(上图的mq也不是必须的)
主动服务的业务变更存在本地数据库中,然后把被动数据库的数据变更也存到本地消息部分的数据库中,这个两个保存在一个事务中,在这个事务提交前 发送消息数据给 消费者(备注,这个发送方法可以发送失败,但是不能抛出异常)
这时候本地消息状态是发送中,如果 消费方 正常的 处理 了消息数据,告诉 主动方,主动方的这条消息标记为 完成。如果 消费方 没有正常的响应 ,那么重发 机制 会重发那这条消息。
因为消息有重发,可能重复处理,所以要求消费方幂等的处理 消息数据