【进阶之路】可靠消息最终一致性解决方案

4,662 阅读8分钟

导言

大家好,我是南橘,从接触java到现在也有差不多两年时间了,两年时间,从一名连java有几种数据结构都不懂超级小白,到现在懂了一点点的进阶小白,学到了不少的东西。知识越分享越值钱,我这段时间总结(包括从别的大佬那边学习,引用)了一些平常学习和面试中的重点(自我认为),希望给大家带来一些帮助

这是消息中间件的文章,大家没有看过的可以跟着看一下

有需要的同学可以加我的公众号,以后的最新的文章第一时间都在里面,也可以找我要思维导图

上一章,讲了分布式系统中的柔性事务解决方案,并且介绍了一下2PC、3PC、与TCC解决方案。这一次,给大家介绍一种可靠消息最终一致性解决方案,用来实现分布式事务。

1、可靠消息最终一致性事务

可靠消息最终一致性方案是指当事务发起执行完全本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发给事务参与方最终事务要达到一致

2、实现可靠消息最终一致性方案

因为RocketMQ支持分布式消息,所以这个方案就用RocketMQ来实现。其实用什么消息中间件都只是业务上的选项,重要的是原理。

应某位同学的要求,我自己画了一张图。

  • 生产者生产消息
  • 消息确认服务器确认消息状态
  • RocketMQ传递消息
  • 消费者消费消息

流程:

  • 1、生产者发送消息给消息确认服务器,消息确认服务器存储消息,并且把消息状态改为待确认。
  • 2、生产者发送消息后执行本地数据库,执行成功则确认消息,失败则删除消息。
  • 3、此时如果是确认消息,那么消息确认服务器就把数据库里的消息状态更新为“已发送”,同时将消息发送给MQ。

如果数据库里更新消息的状态失败了,那么就抛异常退出了,就别投递到MQ。

如果投递MQ失败报错了,那么就要抛异常让本地数据库事务回滚。

这俩操作必须得一起成功,或者一起失败。

  • 4、消费者一直等着从MQ消费消息,如果消费到了消息,那么就操作自己本地数据库
  • 5、如果操作成功了,就反过来通知消息确认服务器,说自己处理成功了,然后消息确认服务器就会把消息的状态设置为“已完成”。

流程是不是很简单?但是要保证消息的最终一致性和可靠性,还是有不少的工作要做的。

3、如何保证生产者服务对消息的可靠投递

要保证生产者消息的可靠投递,生产者需要将消息存入自己的数据库中,根据据自己的执行结果,调用可靠消息服务的接口。

如果本地数据库操作执行成功了,那么就找可靠消息服务确认那条消息。如果本地数据库操作失败了,那么就找可靠消息服务删除那条消息。

同时,在消息确认服务器里开发一个后台定时运行的线程,不停的轮询检查各个消息的状态

如果上游服务操作完本地数据库之后,通知可靠消息服务确认消息或者删除消息的时候,出现了问题。这种情况,消息服务器中的消息一直是“待确认”状态,就认为这个消息出了点什么问题,可以回调生产者提供的一个接口,确认生产者数据库操作状态。

如果上游服务器执行成功了,那么可靠消息服务将消息状态修改为“已发送”,同时投递消息到MQ。

如果上游服务执行失败,那么可靠消息服务将数据库中的消息删除即可。

通过这套机制,就可以保证,可靠消息服务一定会尝试完成消息到MQ的投递。

4、如何保证消费者服务器对消息的可靠接收

要保消费者消息的可靠接受,也需要在消息确认服务器里开发一个后台定时运行的线程,通过这个线程,不停的轮询检查各个消息的状态

如果消息状态一直是“已发送”的状态,始终没有变成“已完成”,那么就说明消费者服务始终没有处理成功,这个时候消确认服务器就可以再次尝试重新投递消息到MQ,让消费者服务再次处理。

当然,为了保证数据的一致性,不至于出现重复消费的情况,幂等性的实现是必不可少的。

5、保证消息传递机制的高可用性

一、RabbitMQ的高可用

我们之前的文章讲过,RibbitMQ可以通过搭建集群来实现高可用,但是搭建不同的集群有不同的优劣,这里就不再重复水字数了。

【进阶之路】消息队列——RabbitMQ原理(二)

二、RocketMQ的高可用

RocketMQ是一款出生在高并发分布式时代的消息中间件,所以他本身就是支持高并发和事务的。同时,Name Server无状态,可线性扩展,天然支持高可用。

  • 1、多Master模式

多个Master节点组成的集群,即使有一个节点宕机,对于整个集群来说也没有什么影响。

缺点:单个Master节点宕机期间, 未被消费的消息在节点恢复之前不可用, 消息的实时性就收到影响。当然,使用同步技术可以让消息在各个节点之间同步,只是会导致效能和空间利用大幅下降,

  • 2、多Master多Slave异步复制模式

在多Master的基础上, 每个节点都有至少一个的Slave,Master节点可读可写,但是Slave节点只读不写, 这种情况似于MySQL的主备模式。

优点:单个Master节点宕机期间, Slave节点依旧可以读取消息。

缺点:异步复制的同步方式有可能导致消息丢失。

  • 3、多Master多Slave同步双写模式

与多Master多Slave异步复制模式类似, 区别在于Master和Slave之间的数据通过同步的方式传输。

优点: 数据与服务都无单点,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高。

缺点: 性能比异步复制模式稍低,大约低10%左右,发送单个消息的RT会稍高,目前主宕机后,备机不能自动切换为主机,后续会支持自动切换功能。

三、Kafka 的高可用性

kafka本身是由多个broker组成,每个broker就是一个节点;创建一个topic,这个topic可以划分为多个partition,每个partition可以存在于不同的broker上,每个partition存放放一部分数据。

所以kafka就是一个分布式消息队列,一个topic的数据,是分散放在多个机器上的,每个机器就放一部分数据。

(找张图片来展示一下)

kafka 0.8以后,提供了HA机制,就是replica副本机制。kafka会均匀的将一个partition的所有replica分布在不同的机器上,提高容错性。若某个broker宕机了,刚好其上有某个partition的leader,那么此时kafka会自动重新选举出一个新leader,继续读写那个新leader即可。

写数据的时候:生产者就是leader,将数据落地写入本地磁盘,接着其他follower主动从leader来拉数据。一旦所有follower同步好数据,会发送ack给leader,leader收到所有follower的ack后,会返回写成功的消息给生产者。

消费的时候:只会从leader读,但是只有一个消息已经被所有follower都同步成功返回ack的时候,这个消息才会被读到,即leader和所有follower上都有了这个消息。

结语

这篇文章基本上就是对于分布式事务的一次总结了。我觉得如果大家想更加深入的了解,可以和我一样画画架构图,然后尝试着自己搭建一个简单的服务。这样效果真的不错,虽然在实际工作中大部分公司已经搭建了自己的框架,不过总是要不断更新的~总有一天我们也会独当一面不是?

同时需要思维导图的话,可以联系我,毕竟知识越分享越香!