本篇总结了RocketMQ在发送消息过程中的各方面细节:
一、MQ消息发送的可靠性问题与解决
对于消息的安全性在前面MQ篇(一)已经介绍过,主要涉及:
- 消息确认机制(贯穿整个发送过程)
- 消息在MQ队列中的持久化(MQ正确保存)
- 消息重试机制(为了保证发送到消费者)
但是这个做法并不能保证消息在此过程中正确送达,所以催生下面的细节问题。
二、MQ对消息的持久化
一、中涉及了MQ在队列中对消息进行保存,下面分析为什么要持久化数据:
如果MQ不对消息进行持久化保存,那么从生产者发送给消费者的消息
- 一旦MQ发生宕机,下次重启之后消息将全部消失(保证消息不丢失的角度考虑)
- 如果不进行持久化保存,那么有可能造成生产者将消息重复发送给MQ(生产者发送消息给MQ,MQ由于网络原因并未返回确认给生产者,那么生产者重复发送,于是MQ队列中存放两条相同的消息,进而消费者重复消息)(重复消费角度的角度考虑)

于是,如何处理能够解决这两个问题?
就是将消息持久化保存到中间数据库中:
- 防止消息在MQ丢失
- 从生产者发送来的消息,首先进行持久化,并且配置了唯一键索引,如果触发唯一冲突,那么该消息已经存在,避免来自生产者消息的重复性

进一步分析:在mysql中,所有的数据其实是保存在文件中(mysql将消息保存在binlog日志文件中),那么这里保存到数据库,其实也是保存到文件:

进而引出下面问题三
三、Rocket采用更高效的持久化技术
既然最终消息保存在文件中,那么不如一开始就不通过数据库,而是直接保存到文件,Rocket通过两种方式提高消息持久化的速度:
1.用顺序写入的方式,数据不是一块一块地写入,而是预先申请一片连续的存储空间,后续直接写入即可

2.零拷贝技术

四、消息的存储结构:
在MQ中,消息被保存到commitlog文件中。这里主要负责以多队列的方式存放消息

在 RocketMQ 中:
所有 Topic 的所有 Queue 的消息,统统写在同一个 CommitLog 文件序列中。
也就是说:
TopicA.Queue0 → 写入 CommitLog
TopicA.Queue1 → 写入 CommitLog
TopicB.Queue0 → 写入 CommitLog
消费者读取消息绑定的是topic,但是消息实际上被保存在topic的queue中,那么消息被push给消费者之后,就需要记录消费者当前读取到了哪个massageQueue的哪条消息。
所以,需要有一个偏移量,记录当前是哪个队列,队列中的哪条消息。
那么既然知道了消息最终是要保存到磁盘中的(保存到内存肯定不安全),那么是立即保存? 还是有规律的保存,这就涉及到刷盘方式
五、刷盘机制
正常情况是消息一旦到来为了安全性直接开启持久化操作:

但是这种方式最耗费资源,所以也存在如“异步刷盘”的方式:

那么当前MQ已经能够尽可能地:
- 保证消息不丢失(持久化消息)
- 同时高效持久化(文件写入方式+刷盘机制)
但是引入了中间件,就需要确保中间件的高可用性(尽可能不宕机),那么如何保证MQ的高可用性?(不经常宕机)
六、MQ的高可用
我们知道mysql为了保证高可用性采用了分库+主从复制,那么其实在这里也可以通过设置MQ集群的方式来保证高可用性:

那么MQ的结构中包含四个角色,因此可以从四个方面来阐述MQ的集群:

对于主从复制,同样有同步复制与异步复制之分,具体按照开发需求使用:

那么开启集群之后,比如MQ(broker就有多个了,生产者往哪个发?就是问题,之前阐述MQ架构的时候,就知道同一个topic可能绑定在不同的broker上,所以生产者发送消息如果broker如果开启了集群,那么其实可以发送到不同的broker上)
此时就涉及到:什么时候忘哪个发?这个做法相信我们已经有想法了,这个时候一般会采用负载均衡的方式:如果某个broker挂掉,还能够有broker接收

对于消费者端,同样存在负载均衡,尽可能要让同一个broker都能指向消费者集群的不同消费者,为了在某个broker挂掉之后,还能够正确发送给消费者。


七、消息重试策略
在之前分析消费从MQ到消费者的过程中,如果消费者返回ACK失败,怎么办? MQ采用了一种重试机制,并将重复尝试但还是失败的消息放入死信队列,交给开发者处理,下面做具体分析:
这个过程实际上分为:
- 顺序消息重试
- 无序消息重试
如果是顺序消息,重复发送仍然失败,导致队列的offset不发生变化,就可以对此进行监控,通知开发者处理。

如果是无序消息,就采用之前的放入死信队列的方式:

当消息消费重试到达了指定次数(默认16次)后,MQ将无法被正常消费的消息称为死信消息(Dead-LetterMessage)死信消息不会被直接抛弃,而是保存到了一个全新的队列中,该队列称为死信队列(Dead-LetterQueue)
死信队列特征
- 归属某一个组(GourpId),而不归属Topic,也不归属消费者
- 一个死信队列中可以包含同一个组下的多个Topic中的死信消息
- 死信队列不会进行默认初始化,当第一个死信出现后,此队列首次初始化
死信队列中消息特征
- 不会被再次重复消费
- 死信队列中的消息有效期为3天,达到时限后将被清除
1953

被折叠的 条评论
为什么被折叠?



