RocketMQ高可用性实现原理

RocketMQ

(一)特性

  1. 支持Pub/Sub和P2P两种消息模型
  2. 可靠的FIFO队列,严格保证消息有序
  3. 支持Pull/Push两种消息模式
  4. 单一队列百万消息堆积能力
  5. 支持多种消息协议,如JMS
  6. 分布式、高可用
  7. Docker镜像云集群部署
  8. 功能丰富的Dashboard

(二)术语

  • Producer:生产者将消息发送到队列
  • Producer Group:发送同一类消息的生产者组
  • Consumer:消费者从队列消费消息
  • Consumer Group:消费同一类消息的消费者组
  • Topic:主题是消息的逻辑分类,主要用于区分业务模块,比如购物车、订单……
  • Tag:标签是对主题的进一步细分,在相同的业务模块内引入标签标记不同用途的消息
  • Message:消息必须指定Topic,可选Tag以便消费端根据Tag过滤消息
  • Broker:相当于MQ,用于接收生产者的消息,存储消息,并为消费者拉取消息做好准备
  • Name Server:为Producer和Consumer提供路由信息

(三)高可用架构

  1. Name Server集群:提供轻量级的服务发现和路由。每个Name Server记录完整的路由信息,提供等效的读写服务,并支持快速存储扩展。

    Name Server之间互不通信,会存在数据不一致的情况。

RocketMQ为什么使用Name Server而非ZooKeeper
因为ZooKeeper为了保证强一致性会放弃一段时间内的可用性,而Name Server作为注册中心只是为了发现组件地址,对一致性要求不高。

  1. Broker集群:
  • 提供轻量级的Topic和Queue机制来处理消息存储
  • 同时支持Pull/Push模式
  • 支持多Master多Slave异步复制/同步双写机制,防止单点故障
  • 每一个Master和其Slave之间通过主从复制进行数据同步;
  • 每个Broker与Name Server集群中的所有节点建立长连接,定时将Topic信息注册到所有Name Server
  1. Producer Group集群:
  • Producer与Name Server中的任意一个节点建立长连接,定是从Name Server获取Topic路由信息

    请参考ZKMySQL的集群,凡是涉及到“改数据”的都只会连接Master,即只连接写节点,其他节点只是同步复制Master,这样可以保证数据一致性的实现更简单。

  • Producer只能和Broker Master建立长连接,定时发送心跳

  1. Consumer Group集群:可以和Broker Master或Broker Slave建立长连接,获取消息;同一个Consumer Group下的多个Consumer均摊消费消息,如果设置为广播模式,每个Consumer都消费全量数据

高可用

1.消息优先级

优先级是指在一个MQ中,每条消息都有自己的优先级,一般用整数描述,优先级高的消息先投递(由于MQ采用的是FIFO队列,所以一般是通过将消息按照优先级排序后再发送)。

对于优先级问题可以归结为两类:

  1. 严格的优先级:优先级用整数表示,投递前按照优先级对消息进行排序,十分耗时。使用时应该要考虑严格的优先级是否是业务一定需要的,否则不推荐这种。

    RocketMQ所有消息都是持久化的,如果按照严格的优先级,对所有消息进行排序,开销会非常大。

  2. 非严格意义上的优先级:将优先级划分为高、中、低,每个优先级用不同的Topic表示,RocketMQ采用的就是这种方式,单独配置一个高优先级队列和一个低优先级队列,将不同优先级的消息发送到不同队列。


2.消息过滤器

(1)Broker端消息过滤

在Broker端,根据Consumer的要求做过滤。

RocketMQ支持根据Message Tag进行简单的过滤,也支持按照Message Header/Body做过滤

  • 优点:减少了对于Consumer无用消息的网络传输
  • 缺点:增加了Broker负担,实现相对复杂
(2)Consumer端消息过滤

在Consumer端进行消息过滤。

  • 优点:可以完全自定义实现
  • 缺点:会有很多无用消息要传输到Consumer端

3.消息持久化

MQ通常支持的集中消息持久化方式:

  1. 持久化到数据库

  2. 持久化到K/V存储

  3. 文件记录形式持久化,如KafkaRocketMQ

    消息持久化部分的性能直接决定了整个消息中间件的性能,RocketMQ充分利用了Linux文件系统内存Cache来提高性能。

  4. 对内存数据做持久化镜像


4.消息可靠性

影响消息可靠性的几种情况:

  1. Broker正常关闭
  2. Broker异常Crash
  3. OS Crash
  4. 机器断点,但是可以恢复

    以上4种情况都属于硬件资源可恢复的情况,RocketMQ可以通过消息持久化保证消息不丢失,或者只丢失少部分。

如果写入磁盘和返回Consumer ACK这两个操作是同步的,那么可以做到消息完全不丢失;如果是异步的,那么会丢失少部分。

同步刷盘怎么才能快?
1.使用DirectBuffer堆外内存,加快内存拷贝
2.数据和索引分离,通过Offset快速定位,减少I/O随机读写性能损耗

  1. 机器损坏
  2. 磁盘损坏

    5和6属于单点故障,一旦发生此单点上的消息全部丢失。
    RocketMQ通过多Master之间的异步复制保证99%的消息不会丢失,但是仍有少量消息可能丢失。
    可以通过同步双写完全避免单点故障,但是同步双写势必影响性能,只适合对消息可靠性要求非常高的情况,比如Money


5.消息低延迟

消息到达Broker后,在不堆积的情况下,必须要尽快到达Consumer。可以有两种形式:

  1. Broker Push
  2. Consumer Pull:RocketMQ采用的是长轮询Pull,可以保证消息实时性

6.每个消息必须投递至少一次

RocketMQ Consumer先将消息Pull到本地,消费完成后,才向服务器返回ACK,可以保证每个消息必须投递至少一次。


7.每个消息只被处理一次

这个特性是说:

  1. 发送消息阶段,不允许发送重复的消息;
  2. 消费消息阶段,不允许消费重复的消息

要在分布式系统中满足以上两点,势必会产生巨大的开销。所以RocketMQ为了追求高性能,没有实现此特性,而是要求业务上进行去重,也就是保证消费消息的幂等性


8.Broker Buffer满了如何处理

通常来说Broker Buffer指的是Broker中一个队列的内存Buffer大小,这类Buffer通常大小有限,如果Buffer满了可以有拒绝消息、丢弃队首消息等措施,类似线程池的拒绝策略

但是RocketMQ没有内存Buffer的概念,它的队列都是持久化磁盘,数据定期清除;不过RocketMQ使用LinkedBlockingQueue作为消息队列,理论上是无限大,但是可能导致内存泄漏


9.回溯消息

回溯是指Consumer已经消费了的消息,如果业务上需要重新消费,需要支持此功能,RocketMQ的消息都是持久化的,支持通过时间维度进行回溯。


10.消息堆积

  1. 消息堆积在内存Buffer:消息堆积能力取决于内存Buffer大小;
  2. 消息堆积在持久化存储系统,如磁盘:当消息不能在内存Cache命中时,要不可避免地访问磁盘,会产生大量的I/O,读I/O的吞吐量直接决定了消息堆积后的访问能力


RocketMQ采用了一种数据和索引分开的存储方式,有效降低I/O销毁。


11.分布式事务

RocketMQ的分布式事务采用的是二阶段提交(2PA)

  1. 2PA在数据存储方面需要KV存储的支持,因为二阶段的提交回滚需要修改消息状态,一定涉及到根据Key查找Message的动作。

    消息回滚了也不会物理删除,只是逻辑删除

  2. RocketMQ在二阶段绕过了KV存储,直接在一阶段发送Prepared消息时拿到了消息的地址Offset,二阶段提交回滚时直接通过Offset访问消息,并修改状态。

    缺点:使用Offset会使得系统的脏页过多

-------------本文结束感谢您的阅读-------------

本文标题:RocketMQ高可用性实现原理

文章作者:DragonBaby308

发布时间:2019年07月31日 - 22:55

最后更新:2020年01月25日 - 12:35

原始链接:http://www.dragonbaby308.com/rocketmq/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

急事可以使用右下角的DaoVoice,我绑定了微信会立即回复,否则还是推荐Valine留言喔( ఠൠఠ )ノ
0%