RocketMq

使用建议

  1. 不建议同一个消费者组订阅多个Topic,因为 RocketMQ 是按照 Topic 维度进行 rebalanace: https://cloud.tencent.com/developer/article/1554950
  2. 要注意使用自定义的负载均衡算法选择队列后,RocketMQ 的重试机制将失效、
  3. 生产环境中,autoCreateTopicEnable 不能设置为 true

消费方式

在 RocketMQ 里消费方式虽有 PUSH(主动推送给消费者) 与 PULL(消费者主动取 broker 拉取) 两种,但实现机制实为 PULL 模式,PUSH 模式是一种伪推送,是对 PULL 模式的封装,每拉取一批消息后,提交到消费端的线程池(异步),然后马上向 Broker 拉取消息,即实现类似"推"的效果。

但是为了能做到实时收消息,RocketMQ 使用长轮询方式,可以保证消息实时性与 Push 方式一致

消费者线程池默认拒绝策略是 AbortPolicy(抛出异常)

区别对比

  1. push:实时性高,但增加服务端负载,消费端能力不同,如果 push 速度过快,消费端会出现问题
  2. pull:可控性好,但时间间隔不好设置,间隔太长,消息不能及时处理,间隔太短,空请求会多

一、RocketMQ 支持 3 种消息发送方式 :

  1. 同步消息(sync message )

producer向 broker 发送消息,执行 API 时同步等待, 直到broker 服务器返回发送结果 。

  1. 异步消息(async message)

producer 向 broker 发送消息时指定消息发送成功及发送异常的回调方法,调用 API 后立即返回,producer发送消息线程不阻塞 ,消息发送成功或失败的回调任务在一个新的线程中执行 。

  1. 单向消息(oneway message)

producer向 broker 发送消息,执行 API 时直接返回,不等待broker 服务器的结果 。

RocketMQ架构原理解析(一):整体架构

消息存储

消息存储和下面三个文件关系非常紧密:

  1. 数据文件 commitlog
    消息主体以及元数据的存储主体 ;
  2. 消费文件 consumequeue
    消息消费队列,引入的目的主要是提高消息消费的性能。消费队列文件的格式为"./consumequeue/Topic名字/queue id/具体消费队列文件"。每个消费队列其实是commitlog的一个索引,提供给消费者做拉取消息、更新位点使用。
  3. 索引文件 indexfile
    索引文件,提供了一种可以通过 key 或时间区间来查询消息,全部的文件都是按照消息 key 创建的 hash 索引,文件名是用创建时的时间戳命名。
  4. config

保存了当前 Broker 中全部的 topic、订阅关系和消费进度,这些数据 Broker 会定时从内存持久化到磁盘,以便宕机后恢复。

  1. abort

Broker 是否异常关闭的标志,正常关闭时该文金啊会被删除,当 Broker 重新启动时,根据是否异常宕机决定是否要重新构建 index 索引等操作

  1. checkpoint

Broker 最近一次正常运行时的状态,比如最后一次正常刷盘的时间、最后一次正确索引的时间等

RocketMQ 采用的是混合型的存储结构,Broker 单个实例下所有的队列公用一个数据文件来存储(commitlog)。

生产者发送消息到 Broker 端,然后 Broker 端使用同步或者异步的方式对消息刷盘持久化,保存至 commitlog 文件中,只要消息保存到 commitlog 中,那么消息生产者发送的消息就不会丢失

Brocker 端的后台服务线程会不停的分发请求并构建 consumequeue(消费文件)和indexfile(索引文件)。

数据文件 commitlog

所有消息都会顺序写入数据文件,当文件写满,会写入下一个文件。

单个文件大小默认 1G , 文件名长度为 20 位,左边补零,剩余为起始偏移量,比如 00000000000000000000 代表了第一个文件,起始偏移量为 0 ,文件大小为 1 G = 1073741824。当第一个文件写满了,第二个文件为 00000000001073741824,起始偏移量为 1073741824,以此类推。

消费文件 consumequeue

  1. 消费文件按 topic 存储,每个 topic 一个文件夹,文件夹下有不同的队列,队列轻量化,单个队列数据非常少
  2. consumequeue 只存储消息在 commitlog 的位置信息,并且串行方式刷盘, 对磁盘访问串行化,避免磁盘竞争,不会导致 IOWAIT 增高,文件的名称也是以偏移量来命名的,可以通过消息的逻辑偏移量定位消息位于哪一个文件里
  3. ConsumeQueue 中存储的每条数据大小是固定的,总共 20 个字节,数据结构如下图
  4. 每个队列目录下,存储 consumequeue 文件,每个 consumequeue 文件也是顺序写入,但是 读取却成了随机读
  5. 消费者从 brocker 获取订阅消息数据时,不用遍历整个 commitlog 文件,只需要根据逻辑偏移量从 consumequeue 文件查询偏移量了,最后定位到 commitlog 获取到真正的消息数据,增加了读取开销

如何克服以上缺点(红色标识)

  1. 随机读尽可能让读命中 PageCache,减少 IO 读操作,所以内存越大越好。如果系统中堆积的消息过多,读数据要访问磁盘并不会由于随机读取导致性能急剧下降

    1. 访问 PageCache 时,即使只访问 1K 的数据,系统也会提前预读出更多数据,在下次读取时,就可能命中内存
    2. 随机访问 CommitLog 磁盘数据,系统 IO 调度算法设置为 NOOP 算法,会在一定程度上将完全随机的读变为顺序跳跃方式,而顺序跳跃方式读较完全的随机读性能会提高 5 倍以上
  2. 由于 ConsumeQueue 存储数据量极少,而且是顺序读,在 PageCache 预读作用下,ConsumeQueue 的读性能几乎与内存一致,所以可认为 ConsumeQueue 完全不会阻碍读性能

  3. RocketMQ持久化先写入系统 pageCache,然后刷盘,可以保证内存与磁盘都有一份数据,访问时,直接从内存读取

索引文件 indexfile

消息在业务层面的唯一标识码要设置到 keys 字段,方便定位消息丢失问题,服务器会为每个消息创建索引(hash 索引),应用可以通过 topic、key 来查询这条消息内容,以及消息被谁消费。

  1. 索引文件名 fileName 是以创建时的时间戳命名的,固定的单个 IndexFile 文件大小约为 400 M

消息构成

索引机制

  1. 基于 MessageId 提取消息

messageId(服务端的) 是用 broker + offset 生成的,所以很容易找到对应的 commitLog 读取消息

  1. 基于 tag 查询

    1. 消费者对想要提取的 tag = 20220101 进行 hash,获取 hash 值 99
    2. 在 ConsumeQueue 文件中查找对应 hash(tag) = 99 的 offset 数据
    3. 根据物理位置 offset 到对应的 commitlog 文件提取消息,因为存在 hash 碰撞,所以对命中数据再进行字符串匹配方式筛选 "20220101" 的消息
    4. 提取消息,封装成 Message 对象返回
  1. 每个 IndexFile 文件包含文件头、hash 槽位、索引数据,每个文件的 hash 槽位个数、索引数据个数都是固定的,hash 槽位可以通过 Broker 启动参数 maxHashSlotNum 进行配置,默认 500W ,索引数据可以通过 maxIndexNum 进行配置,默认值是 500 ✖ 4 = 2000W,一个 IndexFile 约为 400MB

消息处理原理

消息过滤

  1. 在 Broker 端进行 MessageTag 比对,如果存储的 MessageTag 与订阅的 MessageTag 不符合,则跳过,符合则传输给 Consumer。注意:MessageTag 是字符串形式,ConsumeQueue 中存储的是对应的 hashCode,对比时对比的也是 hashCode
  2. Consumer 收到过滤后的消息后,同样也要执行在 Broker 端的操作,但是对比的是真是的字符串,而不是 HashCode

为什么要这样做?

  1. 过滤过程中不会访问 CommitLog 数据并且 hashCode 是数字比较,可以保证堆积情况下也能高效过滤
  2. 及时存在 hash 冲突,也可以在 Consumer 端继续宁修正,保证万无一失

RocketMQ一个消费组内订阅同一个主题不同的TAG为什么会丢消息

消息消费

  1. 如果从commitlog文件查找消息时,发现消息堆积太多,默认超过物理内存的40%后,会建议从从服务器读取
  2. 如果当前服务器的角色为从服务器:并且slaveReadEnable=true,则下次拉取切换为从主拉取。
  3. 如果slaveReadEnable=true(从允许读),并且建议从从服务器读取,则从消息消费组建议当消息消费缓慢时建议的拉取brokerId,由订阅组配置属性whichBrokerWhenConsumeSlowly决定;如果消息消费速度正常,则使用订阅组建议的brokerId拉取消息进行消费,默认为主服务器。如果不允许从可读,则固定使用从主拉取。

消息同步

  1. 消息消费进度的同步是单向的,从服务器开启一个定时任务,定时从主服务器同步消费进度
  2. 无论消息消费者是从服务器拉取的消息还是从主服务器拉取的消息,在向 Broker 反馈消息时,优先向主服务器汇报
  3. 消息消费者向从服务器拉取消息时 ,如果消息消费者内存中存在消息消费进度时,主服务器会尝试更新消息消费进度,这样一来,消费端与主服务器只挂了器中一个,并不会导致消息重新被消费

刷盘策略

同步刷盘与异步刷盘的唯一区别是异步刷盘写完 PAGECACHE 直接返回,而同步刷盘需要等待刷盘完成才返回,

同步刷盘流程如下:

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

问题

和 kafka 的区别

  1. kafka 使用 Zookeeper 作为注册中心
  2. kafka 针对消费者和生产者使用了同一份存储结构,每个 partition 对应一个文件,收到消息后 kafka 会把数据插入到文件末尾
  3. RocketMQ 针对消费者和生产者使用了不同的存储结构,消费者对应 CommitLog,消费者对应 ConsumeQueue,RocketMQ 是把所有 topic 的所有 queue 消息存储在一个文件(commitlog)里,然后分发给 ConsumeQueue
  4. kafka 在大数据量传输、IO 并行写入能力较强,但大量 topic 下由于并行度、CPU 切换等问题,导致服务器性能下降
  5. RocketMQ 是按照 Topic 维度进行 rebalanace,而 kafka 是将所有 topic 放在一起
  6. Consumer 消费消息过程,使用了零拷贝,RocketMQ 选择了第一种方式,mmap+write 方式,因为有小块数据传输的需求,效果会比 sendfile 更好,而 RocketMQ 是针对的业务场景, kafka 针对的是大数据场景。(出自《RocketMQ 原理简介》v3.1.1,Alibaba 淘宝消息中间件项目组)零拷贝包含以下两种方式
    1. 使用 mmap + write 方式
      1. 优点:即使频繁调用,使用小块文件传输,效率也很高
      2. 缺点:不能很好的利用 DMA 方式,会比 sendfile 多消耗 CPU,内存安全性控制复杂,需要避免 JVM Crash 问题。
    2. 使用 sendfile 方式
      1. 优点:可以利用 DMA 方式,消耗 CPU 较少,大块文件传输效率高,无内存安全新问题。
      2. 缺点:小块文件效率低于 mmap 方式,只能是 BIO 方式传输,不能使用 NIO。

为什么 RocketMQ 不使用 Zookeeper

  1. topic路由信息无须在集群之间保持强一致,而是追求最终一致性,而Zookeeper 满足 CP 原则,NameServer 集群之间互不通信,性能相较于 Zookeeper 有极大提升
  2. kafka 使用 Zookeeper 是因为 kafka 的 Master/Slave 是选出来的,而 RocketMq 基于 raft 协议的 DLedger 自动主从切换,完全可以不依赖 Zookeeper
  3. 引入 Zookeeper 增加运维复杂性

RocketMQ 参数

www.cnblogs.com/zhyg/p/1025...

useEpollNativeSelector:是否启用Epoll I/O模型,Linux 环境下建议开启

相关推荐
白开水23321 小时前
Apache RocketMQ 5.1.3安装部署文档
apache·rocketmq
RemainderTime6 天前
(七)Spring Cloud Alibaba 2023.x:RocketMQ 消息队列配置与实现
后端·spring cloud·微服务·rocketmq
你啊我啊你好7 天前
常用的消息中间件(ActiveMQ、RabbitMQ、RocketMQ、Kafka)面试精华
rabbitmq·rocketmq·activemq
Hello-Brand7 天前
RocketMQ系列3:核心技术介绍
消息队列·rabbitmq·rocketmq·mq·消息中间件·削峰填谷
_BugMan8 天前
【业务场景】订单超时取消的各种方案
redis·java-ee·rocketmq
Ch.yang12 天前
【架构】从 Socket 的角度认识非阻塞模型
tomcat·rocketmq
码农老起12 天前
从RocketMQ到Dubbo:自研中间件技术的崛起
中间件·dubbo·rocketmq
Steven_Mmm14 天前
关于消息队列性能是否能接收 5 万个 MQ
kafka·rocketmq
小范馆14 天前
RocketMQ面试题合集
java·rocketmq·java-rocketmq
菜菜-plus14 天前
Kafka | RabbitMQ | RocketMQ | ActiveMQ 的区别和入门案例
kafka·rabbitmq·rocketmq