@@ -3435,6 +3435,8 @@ RCVBUF_ALLOCATOR:属于 SocketChannal 参数
34353435
34363436### 消息队列
34373437
3438+ #### 应用场景
3439+
34383440消息队列是一种先进先出的数据结构,常见的应用场景:
34393441
34403442* 应用解耦:系统的耦合性越高,容错性就越低
@@ -3459,6 +3461,36 @@ RCVBUF_ALLOCATOR:属于 SocketChannal 参数
34593461
34603462
34613463
3464+ ***
3465+
3466+
3467+
3468+ #### 技术选型
3469+
3470+ RocketMQ 对比 Kafka 的优点
3471+
3472+ * 支持 Pull 和 Push 两种消息模式
3473+
3474+ - 支持延时消息、死信队列、消息重试、消息回溯、消息跟踪、事务消息等高级特性
3475+ - 对消息可靠性做了改进,** 保证消息不丢失并且至少消费一次** ,与 Kafka 一样是先写 PageCache 再落盘,并且数据有多副本
3476+ - RocketMQ 存储模型是所有的 Topic 都写到同一个 Commitlog 里,是一个 append only 操作,在海量 Topic 下也能将磁盘的性能发挥到极致,并且保持稳定的写入时延。Kafka 的吞吐非常高(零拷贝、操作系统页缓存、磁盘顺序写),但是在多 Topic 下时延不够稳定(顺序写入特性会被破坏从而引入大量的随机 I / O ),不适合实时在线业务场景
3477+ - 经过阿里巴巴多年双 11 验证过、可以支持亿级并发的开源消息队列
3478+
3479+ Kafka 比 RocketMQ 吞吐量高:
3480+
3481+ * Kafka 将 Producer 端将多个小消息合并,采用异步批量发送的机制,当发送一条消息时,消息并没有发送到 Broker 而是缓存起来,直接向业务返回成功,当缓存的消息达到一定数量时再批量发送
3482+
3483+ * 减少了网络 I / O ,提高了消息发送的性能,但是如果消息发送者宕机,会导致消息丢失,降低了可靠性
3484+ * RocketMQ 缓存过多消息会导致频繁 GC ,并且为了保证可靠性没有采用这种方式
3485+
3486+ Topic 的 partition 数量过多时,Kafka 的性能不如 RocketMQ :
3487+
3488+ * 两者都使用文件存储,但是 Kafka 是一个分区一个文件,Topic 过多时分区的总量也会增加,过多的文件导致对消息刷盘时出现文件竞争磁盘,造成性能的下降。一个分区只能被一个消费组中的一个消费线程进行消费,因此可以同时消费的消费端也比较少
3489+
3490+ * RocketMQ 所有队列都存储在一个文件中,每个队列存储的消息量也比较小,因此多 Topic 的对 RocketMQ 的性能的影响较小
3491+
3492+
3493+
34623494****
34633495
34643496
@@ -4555,7 +4587,7 @@ RocketMQ 消息的存储是由 ConsumeQueue 和 CommitLog 配合完成 的,Com
45554587* ConsumerQueue :消息消费队列,存储消息在 CommitLog 的索引。RocketMQ 消息消费时要遍历 CommitLog 文件,并根据主题 Topic 检索消息,这是非常低效的。引入 ConsumeQueue 作为消费消息的索引,** 保存了指定 Topic 下的队列消息在 CommitLog 中的起始物理偏移量 offset** ,消息大小 size 和消息 Tag 的 HashCode 值,每个 ConsumeQueue 文件大小约 5. 72M
45564588* IndexFile :为了消息查询提供了一种通过 Key 或时间区间来查询消息的方法,通过 IndexFile 来查找消息的方法** 不影响发送与消费消息的主流程** 。IndexFile 的底层存储为在文件系统中实现的 HashMap 结构,故 RocketMQ 的索引文件其底层实现为 ** hash 索引**
45574589
4558- RocketMQ 采用的是混合型的存储结构,即为 Broker 单个实例下所有的队列共用一个日志数据文件(CommitLog )来存储。混合型存储结构( 多个 Topic 的消息实体内容都存储于一个 CommitLog 中)针对 Producer 和 Consumer 分别采用了** 数据和索引部分相分离** 的存储结构,Producer 发送消息至 Broker 端,然后 Broker 端使用同步或者异步的方式对消息刷盘持久化,保存至 CommitLog 中。只要消息被持久化至磁盘文件 CommitLog 中,Producer 发送的消息就不会丢失,Consumer 也就肯定有机会去消费这条消息
4590+ RocketMQ 采用的是混合型的存储结构,即为 Broker 单个实例下所有的队列共用一个日志数据文件(CommitLog )来存储, 多个 Topic 的消息实体内容都存储于一个 CommitLog 中。混合型存储结构针对 Producer 和 Consumer 分别采用了** 数据和索引部分相分离** 的存储结构,Producer 发送消息至 Broker 端,然后 Broker 端使用同步或者异步的方式对消息刷盘持久化,保存至 CommitLog 中。只要消息被持久化至磁盘文件 CommitLog 中,Producer 发送的消息就不会丢失,Consumer 也就肯定有机会去消费这条消息
45594591
45604592服务端支持长轮询模式,当消费者无法拉取到消息后,可以等下一次消息拉取,Broker 允许等待 30s 的时间,只要这段时间内有新消息到达,将直接返回给消费端。RocketMQ 的具体做法是,使用 Broker 端的后台服务线程 ReputMessageService 不停地分发请求并异步构建 ConsumeQueue (逻辑消费队列)和 IndexFile (索引文件)数据
45614593
@@ -4593,7 +4625,7 @@ MappedByteBuffer 内存映射的方式**限制**一次只能映射 1.5~2G 的文
45934625
45944626页缓存(PageCache )是 OS 对文件的缓存,每一页的大小通常是 4K,用于加速对文件的读写。因为 OS 将一部分的内存用作 PageCache ,所以程序对文件进行顺序读写的速度几乎接近于内存的读写速度
45954627
4596- * 对于数据的写入,OS 会先写入至 Cache 内,随后通过异步的方式由 pdflush 内核线程将 Cache 内的数据刷盘至物理磁盘上
4628+ * 对于数据的写入,OS 会先写入至 Cache 内,随后 ** 通过异步的方式由 pdflush 内核线程将 Cache 内的数据刷盘至物理磁盘上**
45974629* 对于数据的读取,如果一次读取文件时出现未命中 PageCache 的情况,OS 从物理磁盘上访问读取文件的同时,会顺序对其他相邻块的数据文件进行** 预读取** (局部性原理,最大 128K)
45984630
45994631在 RocketMQ 中,ConsumeQueue 逻辑消费队列存储的数据较少,并且是顺序读取,在 PageCache 机制的预读取作用下,Consume Queue 文件的读性能几乎接近读内存,即使在有消息堆积情况下也不会影响性能。但是 CommitLog 消息存储的日志数据文件读取内容时会产生较多的随机访问读取,严重影响性能。选择合适的系统 IO 调度算法和固态硬盘,比如设置调度算法为 Deadline ,随机读的性能也会有所提升
@@ -4627,8 +4659,6 @@ RocketMQ 采用文件系统的方式,无论同步还是异步刷盘,都使
46274659
46284660
46294661
4630-
4631-
46324662****
46334663
46344664
@@ -4698,7 +4728,9 @@ BrokerServer 的高可用通过 Master 和 Slave 的配合:
46984728
46994729* Slave 只负责读,当 Master 不可用,对应的 Slave 仍能保证消息被正常消费
47004730* 配置多组 Master - Slave 组,其他的 Master - Slave 组也会保证消息的正常发送和消费
4701- * 目前不支持把 Slave 自动转成 Master ,需要手动停止 Slave 角色的 Broker ,更改配置文件,用新的配置文件启动 Broker
4731+ * ** 目前不支持把 Slave 自动转成 Master ** ,需要手动停止 Slave 角色的 Broker ,更改配置文件,用新的配置文件启动 Broker
4732+
4733+ 所以需要配置多个 Master 保证可用性,否则一个 Master 挂了导致整体系统的写操作不可用
47024734
47034735生产端的高可用:在创建 Topic 的时候,把 Topic 的** 多个 Message Queue 创建在多个 Broker 组** 上(相同 Broker 名称,不同 brokerId 的机器),当一个 Broker 组的 Master 不可用后,其他组的 Master 仍然可用,Producer 仍然可以发送消息
47044736
@@ -4716,7 +4748,7 @@ BrokerServer 的高可用通过 Master 和 Slave 的配合:
47164748
47174749如果一个 Broker 组有 Master 和 Slave ,消息需要从 Master 复制到 Slave 上,有同步和异步两种复制方式:
47184750
4719- * 同步复制方式:Master 和 Slave 均写成功后才反馈给客户端写成功状态。在同步复制方式下,如果 Master 出故障, Slave 上有全部的备份数据,容易恢复,但是同步复制会增大数据写入延迟,降低系统吞吐量
4751+ * 同步复制方式:Master 和 Slave 均写成功后才反馈给客户端写成功状态(写 Page Cache ) 。在同步复制方式下,如果 Master 出故障, Slave 上有全部的备份数据,容易恢复,但是同步复制会增大数据写入延迟,降低系统吞吐量
47204752
47214753* 异步复制方式:只要 Master 写成功,即可反馈给客户端写成功状态,系统拥有较低的延迟和较高的吞吐量,但是如果 Master 出了故障,有些数据因为没有被写入 Slave ,有可能会丢失
47224754
@@ -4737,6 +4769,8 @@ RocketMQ 支持消息的高可靠,影响消息可靠性的几种情况:
47374769
47384770后两种属于单点故障,且无法恢复,一旦发生,在此单点上的消息全部丢失。RocketMQ 在这两种情况下,通过主从异步复制,可保证 99 % 的消息不丢,但是仍然会有极少量的消息可能丢失。通过** 同步双写技术** 可以完全避免单点,但是会影响性能,适合对消息可靠性要求极高的场合,RocketMQ 从 3.0 版本开始支持同步双写
47394771
4772+ 一般而言,我们会建议采取同步双写 + 异步刷盘的方式,在消息的可靠性和性能间有一个较好的平衡
4773+
47404774
47414775
47424776****
@@ -4883,7 +4917,7 @@ IndexFile 文件的存储在 `$HOME\store\index${fileName}`,文件名 fileName
48834917
48844918#### 消息重投
48854919
4886- 生产者在发送消息时,同步消息和异步消息失败会重投,oneway 没有任何保证。消息重投保证消息尽可能发送成功、不丢失,但当出现消息量大、网络抖动时,可能会造成消息重复;生产者主动重发、Consumer 负载变化也会导致重复消息。
4920+ 生产者在发送消息时,同步消息和异步消息失败会重投,oneway 没有任何保证。消息重投保证消息尽可能发送成功、不丢失,但当出现消息量大、网络抖动时,可能会造成消息重复;生产者主动重发、Consumer 负载变化也会导致重复消息
48874921
48884922如下方法可以设置消息重投策略:
48894923
@@ -5045,6 +5079,31 @@ public class MessageListenerImpl implements MessageListener {
50455079
50465080
50475081
5082+ ***
5083+
5084+
5085+
5086+ ### 高可靠性
5087+
5088+ RocketMQ 消息丢失可能发生在以下三个阶段:
5089+
5090+ - 生产阶段:消息在 Producer 发送端创建出来,经过网络传输发送到 Broker 存储端
5091+ - 生产者得到一个成功的响应,就可以认为消息的存储和消息的消费都是可靠的
5092+ - 消息重投机制
5093+ - 存储阶段:消息在 Broker 端存储,如果是主备或者多副本,消息会在这个阶段被复制到其他的节点或者副本上
5094+ - 单点:刷盘机制(同步或异步)
5095+ - 主从:消息同步机制(异步复制或同步双写,主从复制章节详解)
5096+ - 过期删除:操作 CommitLog 、ConsumeQueue 文件是基于文件内存映射机制,并且在启动的时候会将所有的文件加载,为了避免内存与磁盘的浪费,让磁盘能够循环利用,防止磁盘不足导致消息无法写入等引入了文件过期删除机制。最终使得磁盘水位保持在一定水平,最终保证新写入消息的可靠存储
5097+ - 消费阶段:Consumer 消费端从 Broker 存储端拉取消息,经过网络传输发送到 Consumer 消费端上
5098+ - 消息重试机制来最大限度的保证消息的消费
5099+ - 消费失败的进行消息回退,重试次数过多的消息放入死信队列
5100+
5101+
5102+
5103+ 推荐文章:https: // cdn.modb.pro/db/394751
5104+
5105+
5106+
50485107****
50495108
50505109
@@ -7374,7 +7433,7 @@ AllocateMappedFileService **创建 MappedFile 服务**
73747433
73757434ReputMessageService 消息分发服务,用于构** 建 ConsumerQueue 和 IndexFile 文件**
73767435
7377- * run():** 循环执行 doReput 方法** ,所以发送的消息存储进 CL 就可以产生对应的 CQ ,每执行一次线程休眠 1 毫秒
7436+ * run():** 循环执行 doReput 方法** ,** 所以发送的消息存储进 CL 就可以产生对应的 CQ ** ,每执行一次线程休眠 1 毫秒
73787437
73797438 ```java
73807439 public void run()
@@ -7566,7 +7625,7 @@ public GetMessageResult getMessage(final String group, final String topic, final
75667625
75677626 `if ((bufferTotal + sizePy) > ... )`:热数据一次 pull 请求最大允许获取 256kb 的消息
75687627
7569- `if (messageTotal > ... )`:冷数据一次 pull 请求最大允许获取 32 条消息
7628+ `if (messageTotal > ... )`:冷数据一次 pull 请求最大允许获取 32 条消息
75707629
75717630* `if (messageFilter != null )`:按照消息 tagCode 进行过滤
75727631
0 commit comments