【RocketMQ入门到精通】— RocketMQ初级特性能力 | Message Reliablity,消息可靠性(不能多也不能丢)如何解决?

时间:2022-11-04 18:04:42

名言警句


任何先进的技术均与魔法无异​

追本溯源

【​​经历了6个月的失踪,我将带着干货终究归来!【RocketMQ入门到精通】​​】


RocketMQ支持消息的高可靠,影响消息可靠性的几种情况:

  1. Broker非正常关闭
  2. Broker异常Crash
  3. OS Crash
  4. 机器掉电,但是能立即恢复供电情况
  5. 机器无法开机(可能是cpu、主板、内存等关键设备损坏)
  6. 磁盘设备损坏

      (1-4)四种都属于硬件资源可恢复情况,RocketMQ在这四种情况下能保证消息不丢,或丢失少量数据(依赖刷盘方式是同步还是异步)。

(5-6)属于单点故障,且无法恢复,一旦发生,在此单点上的消息全部丢失。RocketMQ在这两种情况下,通过HA异步复制,可保证99%的消息不丢,但是仍然会有极少量的消息可能丢失。通过同步双写技术可以完全避免单点,同步双写势必会影响性能,适合对消息可靠性要求极高的场合,例如与Money相关的应用。

注:RocketMQ从3.0版本开始支持同步双写。

消息可靠性很大部分因素取决于数据的持久化,RocketMQ 的所有消息都是持久化的,先写入系统 PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据, 访问时,直接从内存读取。而对应的持久化而言,刷盘的策略又体现的非常重要。

异步刷盘

【RocketMQ入门到精通】— RocketMQ初级特性能力 | Message Reliablity,消息可靠性(不能多也不能丢)如何解决?

在有 RAID 卡,SAS 15000 转磁盘测试顺序写文件,速度可以达到300M每秒左右,而线上的网卡一般都为千兆网卡,写磁盘速度明显快于数据网络入口速度,那么是否可以做到写完内存就向用户返回,由后台线程刷盘呢?

  1. 由于磁盘速度大于网卡速度,那么刷盘的进度肯定可以跟上消息的写入速度。
  2. 万一由于此时系统压力过大,可能堆积消息,除了写入 IO,还有读取 IO,万一出现磁盘读取落后情况,会不会导致系统内存溢出,答案是否定的,原因如下:
  1. 写入消息到 PAGECACHE 时,如果内存不足,则尝试丢弃干净的PAGE,腾出内存供新消息使用,策略是LRU方式。
  2. 如果干净页不足,此时写入PAGECACHE会被阻塞,系统尝试刷盘部分数据,大约每次尝试32 个PAGE​,​来找出更多干净PAGE。综上,内存溢出的情况不会出现。
同步刷盘

【RocketMQ入门到精通】— RocketMQ初级特性能力 | Message Reliablity,消息可靠性(不能多也不能丢)如何解决?

同步刷盘流程如下
  1. 写入PAGECACHE后,线程等待,通知刷盘线程刷盘。
  2. 刷盘线程刷盘后,唤醒前端等待线程,可能是一批线程。
  3. 前端等待线程向用户返回成功。

At least Once(至少一次)

至少一次(At least Once)指每个消息必须投递一次。Consumer先Pull消息到本地,消费完成后,才向服务器返回ack,如果没有消费一定不会ack消息,所以RocketMQ可以很好的支持此特性。

Exactly Only Once(正好一次)

在发送消息阶段,不允许重复发送消息,在消费消息阶段,不允许重复消费消息,要实现上述2点,在分布式环境下,代价巨大。RocketMQ为了确保高性能,不支持Exactly only once。而不支持exactly only once, 则无法保证消息不重复,所以RocketMQ在消费时,要求RocketMQ的consumer在业务上去重,即消费消息时要保证幂等性。正常情况下消息不会重复,通常只有在网络异常时,才可能出现消息重复。


RocketMQ HA(主从同步)

【RocketMQ入门到精通】— RocketMQ初级特性能力 | Message Reliablity,消息可靠性(不能多也不能丢)如何解决?

  1. 启动Master并在指定端口监听。
  2. 客户端启动,主动连接Master,建立TCP连接。
  3. 客户端向服务端拉取消息,如果是第一次拉取的话,先获取本地commitlog文件中最大的偏移量,以该偏移量向服务端拉取消息。如果没有消息可读时会阻塞5s。
  4. 服务端解析请求,并返回一批数据给客户端。
  5. 客户端收到一批消息后,将消息写入本地commitlog文件中,然后向Master汇报拉取进度,并更新下一次待拉取偏移量。
  6. 然后重复第3步。

主,从服务器都在运行过程中,消息消费者是从主拉取消息还是从从拉取?

默认情况下,RocketMQ消息消费者从主服务器拉取,当主服务器积压的消息超过了物理内存的40%,则建议从从服务器拉取。但如果slaveReadEnable为false,表示从服务器不可读,从服务器也不会接管消息拉取。

当消息消费者向从服务器拉取消息后,会一直从从服务器拉取?

不是的。分如下情况:

  1. 如果从服务器的slaveReadEnable设置为false,则下次拉取,从主服务器拉取。
  2. 如果从服务器允许读取并且从服务器积压的消息未超过其物理内存的30%,下次拉取使用的Broker为订阅组的brokerId指定的Broker服务器,该值默认为0,代表主服务器。
  3. 如果从服务器允许读取并且从服务器积压的消息超过了其物理内存的30%,下次拉取使用的Broker为订阅组的whichBrokerWhenConsumeSlowly指定的Broker服务器,该值默认为1,代表从服务器。
主从服务消息消费进是如何同步的?

消息消费进度的同步时单向的,从服务器开启一个定时任务,定时从主服务器同步消息消费进度;无论消息消费者是从主服务器拉的消息还是从从服务器拉取的消息,在向Broker反馈消息消费进度时,优先向主服务器汇报;消息消费者向主服务器拉取消息时,如果消息消费者内存中存在消息消费进度时,主会尝试跟新消息消费进度。