RocketMQ实现原理之NameServer和Broker

1. 基本信息

相关角色

  • Producer:生产者,向指定Topic发送消息
  • Consumer:消费者,从Broker拉取消息消费
  • NameServer:路由注册中心,无状态、可集群部署、节点间无通信,负责管理Broker路由信息
  • Broker:消息存储与中转服务器,接收、存储及投递消息

1.1 核心组件

组件信息

  • NameServer
    • RouteInfoManager:路由信息管理器,管理如下信息:
      • topicQueueTable:Topic下MessageQueue存储的对应Broker信息
      • brokerAddrTable:Broker名称及对应的主备节点地址信息
      • clusterAddrTable:集群下包含的Broker
      • brokerLiveTable:Broker的实时状态,如判断Broker是否存活的最后心跳时间
    • NettyRemotingServer:Netty远程调用器,和Broker进行网络交互
    • ScheduledExecutorService:定时任务线程,每隔10s检查一次brokerLiveServer,若120s未上报心跳则移除
    • KVConfigManager:主要用于管理顺序消息Topic,Key=Topic名称,Value=所属Broker,增删改时会直接持久化
  • Broker
    • 元数据管理
      • TopicConfigManager:管理Topic配置信息,如读写队列数和权限等
      • ConsumerOffsetManager:持久化管理每个消费组的消费进度
      • SubscriptionGroupManager:管理消费者订阅关系,包括重试队列和死信队列
    • 消息存储管理
      • CommitLog:消息物理存储主体
      • ConsumeQueue:消息逻辑消费队列,即MessageQueue
      • IndexFile:消息查询索引
    • 客户端管理
      • ClientManager:管理连接到本Broker所有的Producer和Consumer
      • PullMessageProcessor:处理消费者拉取消息请求
      • SendMessageProcessor:处理生产者发送消息请求
    • 高可用服务:负责Master和Slave之间的数据同步

组件交互

  • NameServer
    • Broker:Broker主动向NameServer发送心跳请求,NameServer被动保存维护Broker信息
    • Producer:从NameServer拉取Topic对应Broker信息和其下所有MessageQueue
    • Consumer:从NameServer拉取Topic对应Broker信息和其下所有MessageQueue
  • Broker
    • Producer:负载均衡后向对应Broker发送消息
    • Consumer:负载均衡后想对应Broker拉取消息

1.2 Topic和MessageQueue管理

Topic创建模式

  • 自动创建:生产不推荐使用
    • 效果:生产者首次发送消息时创建
    • 配置:autoCreateTopicEnable=true
    • 实现方式
      1. Broker启动准备:Broker创建TBW102,默认MessageQueue=8
      2. 获取路由信息:生产者从NameServer查询到路由TBW102
      3. 发送消息:发送时使用TBW102模板替换原Topic
      4. 创建与注册:Broker收到消息后创建Topic,并注册到NameServer
  • 手动模式
    • 集群模式
      • 效果:每组Master Broker创建相同数量MessageQueue
      • 参数:-c
      • 分配机制:轮询机制,不同组别均匀的分配MessageQueue
    • Broker模式
      • 效果:可针对每个Broker指定不同MessageQueue数量
      • 参数:-b

Topic扩容和缩容

  • 原则
    1. 优先保证数据安全稳定:不轻易移动或删除已包含消息的现有队列
    2. 扩展由于收缩:增加队列是自然且安全的操作,而删除队列是需要谨慎处理的
    3. 负载均衡:分配队列时尽可能采用轮询分配方式,实现均匀分布
    4. 读写队列分离:读写队列分离提供了平滑缩容的能力
  • 底层原理
    1. 执行指令:组A的Broker执行修改Topic队列指令
    2. 修改Topic队列信息:修改本地Topic的队列信息
    3. 同步给NameServer:组A的Broker同步修改后的Topic信息给NameServer
    4. 同步Topic队列:组B的Broker从NameServer获取到Topic最新的队列信息
    5. 算法判断:根据扩容和缩容的原则计算分配队列
    6. 执行操作:根据算法分配结果执行扩容或缩容
  • 初始状态:2组Broker,各分配了4个MessageQueue,一共8个MessageQueue
  • 扩容
    • 目的:新增1组Broker及3个MessageQueue
    • 结果:原2组Broker不变,新增的Broker分配3个MessageQueue
  • 缩容
    • 目的:减少2个MessageQueue
    • 操作方式
      1. 减小写队列:减少2个写队列
      2. 等待消费:多出来的队列消息已被消费
      3. 减小读队列:减少2个读队列
    • 结果:2组Broker各减少1个MessageQueue

ConsumeQueue和MessageQueue

  • ConsumeQueue定义:物理存储层面的实际队列
  • MessageQueue定义:逻辑层面的队列,实际不存在,分为写队列和读队列:
    • 写队列:生产者发送消息的队列视图,决定可往哪些队列发送消息
    • 读队列:消费者拉取消息的队列视图,决定可从哪些队列拉取消息
  • 对应关系
    • 创建规则:ConsumeQueue数量和哪些ConsumeQueue可写入消息由写队列决定
    • 生命周期:缩容后,ConsumeQueue不会立即删除,等消息消费完并且过期才删除
    • 可见性:有多少ConsumeQueue可被消费取决于读队列数量

1.3 特殊Topic

  • SCHEDULE_TOPIC_XXXX:延迟消息的特殊Topic,XXXX为对应的延迟等级,5.0之前使用,5.0后使用时间轮取代
  • RMQ_SYS_TRANS_HALF_TOPIC:事务消息中存储半消息的Topic,代表预提交成功
  • RMQ_SYS_TRANS_OP_HALF_TOPIC:事务消息操作记录Topic,记录半消息的提交/回滚操作记录
  • %RETRY%:为每个消费者组创建的消息重试Topic,消息消费失败后写入此Topic等待消费
  • %DLQ%:死信队列Topic,消息经过最大重试次数后仍失败,写入该队列
  • TBW102:自动创建Topic模板的Topic,autoCreateTopicEnable=true时,且Topic不存在以该Topic为模板去创建目标Topic
  • RMQ_SYS_TRACE_TOPIC:消息轨迹Topic,开启消息轨迹跟踪功能时,追踪数据写入该Topic

对应消息类型

  • 普通消息:由生产者确定发送到具体的MessageQueue
  • 顺序消息:由生产者根据业务key指定发送到具体的MessageQueue
  • 延时消息
    • 4.X:指定延迟等级后消息存放到对应的TopicSCHEDULE_TOPIC_XXXX中,再分发给消费者
    • 5.X:保存到同一个MessageQueue中,根据时间轮分配算法给消费者
  • 事务消息
    1. 半消息:消息存储在Topic RMQ_SYS_TRANS_HALF_TOPIC下,预提交成功
    2. 事务确认:生产者向Broker发送Commit或Rollback请求
      • Commit:将半消息转移到Topic下的MessageQueue中
      • Rollback:删除半消息
    3. 事务回查:Broker主动回查生产者消息状态,根据结果执行最终操作

1.4 消息处理流程

  1. 消息接收处理
    1. 获取路由信息:生产者通过写队列得知可往哪些队列发送消息
    2. 接收与验证:Broker接收消息后校验Topic是否存在、消息体是否超限及是否有写入权等;
    3. 刷盘:消息顺序追加到CommitLog文件末尾
    4. 主从同步:若为同步复制,会等待从Broker写入CommitLog成功
    5. 返回结果:向生产者响应SendResult,包含了MessageID
  2. 消息构建:消息写入CommitLog后后台线程完成构建
    1. 构建ConsumeQueue:监听到CommitLog的新增内容,解析出Topic和QueueID,并追加到对应的ConsumeQueue文件
    2. 构建IndexFile:将新增消息的MessageID存储索引文件中
  3. 消费消息
    1. 获取路由信息:消费者通过读队列得知可从哪些队列拉取消息
    2. 拉取消息:消费者指定Topic、QueueID及消费偏移量(offset),offset指向ConsumeQueue的位置
    3. 查询ConsumeQueue:Broker查找到对应文件,并获取消息在CommitLog的物理偏移量
    4. 读取CommitLog:根据物理偏移量从CommitLog获取完整消息内容
    5. 返回消息与确认:消费者获取到消息完成业务处理后,返回一个ACK确认消息,Broker更新ConsumeQueue的消费进度

1.5 Broker配置

  • brokerClusterName:集群名称,决定多个不同Broker组是否在同一集群
  • brokerName:名字相同分配在同一Broker组
  • brokerId:0表示master,大于0标识slave,需唯一
  • brokerRole
    • SYNC_MASTER:master可配,同步复制
    • ASYNC_MASTER:master可配,异步复制
    • SLAVE:从节点角色

2. 持久化

运行时配置存储

  • CommitLog:消息物理存储主体
    • 物理路径示例:./store/commitlog/
    • 作用:所有Topic和队列的消息都按到达顺序混合存储在此
    • 特点:顺序写入;文件滚动;亿G级数据量
    • 文件名称:文件中第一条消息的偏移量
  • ConsumeQueue:消息逻辑消费队列,即MessageQueue
    • 物理路径示例:./store/consumequeue/{TopicName}/{QueueId}/
    • 作用:作为CommitLog的索引,按Topic和MessageQueue分目录存储
    • 特点:固定格式;异步构建;快速定位
    • 文件名称:文件中第一条消息的偏移量
  • IndexFile:消息查询索引
    • 物理路径示例:./store/index/
    • 作用:提供基于Message Key或时间范围的查询能力
    • 特点:哈希索引;固定大小
    • 文件名称:创建文件时的时间戳
  • config:存储运行时元数据和配置
    • 物理路径示例:./store/config/
    • 文件列表
      • consumerOffset.json:记录集群消费模式下每个消费组对每个MessageQueue的消费偏移量;每5s定时持久化
      • subscriptionGroup.json:存储订阅了该Broker上Topic的消费者组信息,如是否允许消、重试次数等;变更时持久化
      • topics.json:存储该Broker上所有Topic配置信息,如名称、读写队列数量等;变更时持久化
      • delayOffset.json:跟踪延时队列拉取进度;每10s定时持久化
  • checkpoint
    • 物理路径示例:./store/checkpoint
    • 作用:存储检测点,记录CommitLog、ConsumeQueue和IndexFile文件最后刷盘时间戳。重启时根据时间戳检查文件完整性
  • abort
    • 物理路径示例:./store/abort
    • 作用:正常关闭时会被删除。若启动时发现有该文件,数据恢复阶段会使用严格校验和修复流程
  • lock
    • 物理路径示例:./store/lock
    • 作用:保证Broker进程对文件访问的互斥,防止并发错误

磁盘清理机制

  • CommitLog
    • 基于时间保留:消息默认保留3天,超过3天的标记为过期,通过fileReservedTime配置
    • 基于定时任务清理:默认每天凌晨4点清理过期消息,通过deleteWhen配置
    • 基于磁盘空间清理
      • 空间警戒(≥75%):立即清理过期消息
      • 强制清理(≥85%):无论消息是否过期,从最旧消息开始强制清理,直到低于阈值
      • 拒绝写入(≥90%):Broker会变为只读,拒绝新消息写入
    • 手动清理过期消息:使用管理工具手动触发清理操作,紧急情况可使用
  • ConsumeQueue和IndexFile:触发CommitLog时同时清理
  • 清理方式:都是以文件为单位删除的
    • CommitLog:文件中最早消息过期就删除该文件;若文件中部分过期部分有效保留
    • ConsumeQueue:文件名偏移量对应的消息被删除即删除该文件,否则保留
    • IndexFile:文件名时间戳大于过期时间即删除,否则保留

3. 高可用

3.1 Broker数据同步

同一组中的主从Broker所有数据都是一致的,Slave是Master的数据副本,包括以下数据:

  1. Topic:Broker包含的Topic
  2. MessageQueue:Topic下所创建的消息队列
  3. 消费者组:消费者组及其中消费者的订阅关系
  4. 消费进度:所有消费者组的消费进度

Broker的主从同步都是slave主动向master发起连接请求或拉取数据,设计核心目的:

  1. 简化设计:角色单一化,只需被动接收请求,避免slave状态对master的干扰
  2. 减轻负担:master主要承担消息的写入职责,需要减少维护slave状态的工作量
  3. 方便扩展:数据同步主动权在slave,扩展时slave只需要管理自身数据即可

主从Broker数据同步机制

  • 消息数据同步
    • 同步内容CommitLog文件中的消息
    • 同步机制:slave通过TCP长连接持续向master上报偏移量(offset),master返回之后的消息
    • 核心目的:保证消息高可靠性高可用性,避免消息丢失
  • 元数据同步
    • 同步内容Topic、消费者偏移量和订阅关系等系统配置信息
    • 同步机制:slave每隔10s向master发起请求同步全量数据并覆盖本地数据
    • 核心目的:保证slave的系统元数据和master保持一致,以便后续能顺利成为master

消息复制机制:master把消息同步给slave

  • 异步复制:性能优先,消息写入CommitLog后即返回结果,后台线程控制消息同步给其它从Broker
  • 同步复制:可靠性优先,消息写入主Broker的CommitLog且等待至少有一个从Broker写入CommitLog后返回结果

集群模式下,所有消费者的ACK都是发送给Master Broker,再通过消息复制机制同步给Slave Broker,广播模式消费进度存储在消费者本地

消息堆积

  • 判定条件:消费者拉取偏移量和CommitLog最大消息偏移量差值是否大于物理内存40%(默认)
  • 触发操作:master会建议该消费者去配置suggestWhichBrokerId对应的slave拉取消息
  • 完整流程
    1. 消费者从master拉取消息时判定为消息堆积,返回建议slave id
    2. 消费者收到建议slave id后更新缓存,后续从slave拉取消息
    3. 消费者消费消息后发送ACK给master
    4. master收到ACK后更新消费进度并同步给slave
    5. slave从master同步元数据
    6. 消费者拉取消息时slave根据元数据判定master不存在消息堆积,返回master id
    7. 消费者收到master id,后续从master拉取消息

3.2 故障恢复

故障恢复方式

  • 手动操作
    • master恢复方式:修改slave配置后重启slave恢复
    • 条件:无,支持1主多从
  • DLedger选举
    • master恢复方式:同组Broker使用DLedger算法自动推举master恢复
    • 条件:至少1主2从,同组Broker数量为奇数

版本划分

  • 4.5之前:仅支持手动操作
  • 4.5+:支持手动操作和DLedger选举

4. 部署信息

启动顺序:先启动所有NameServer,再启动各个Broker

部署原则:同一组Broker优先1主1从,扩容时新增一组Broker,不推荐1主多从

部署场景

  • 需求:2台NameServer,2台主Broker,2台从Broker
  • 机器数量:最少需要2台,如下:
    • node1:NameServer1,Broker-master1,Broker-slave2
    • node2:NameServer2,Broker-master2,Broker-slave1
  • 操作流程
    1. 分别启动NameServer
    2. 配置Broker中的NameServer地址
    3. 启动Broker

扩容方式:

  • 机器分布现状:假设机器分布情况如下:
    • node1NameServer1Broker-master1Broker-slave2
    • node2NameServer2Broker-master2Broker-slave1
  • 扩容NameServer:新增1台NameServer
    1. 启动新增NameServer3
    2. 依次更新原Broker的NameServer列表配置
    3. 空闲时依次更新生产者和消费者的NameServer配置
  • 扩容Broker:分别新增1台主Broker和从Broker
    1. 设置node2机器的从BrokerBroker-slave1为只读,等无消息被消费时进行下一步
    2. node3机器启动新增主BrokerBroker-master3Broker-slave1
    3. node2机器启动新增从BrokerBroker-slave3
    4. 手动分配现有Topic队列到新增Broker
    5. Consumer和Producer通过自动发现机制获取最新拓扑信息
  • 扩容后机器分布
    • node1NameServer1Broker-master1Broker-slave2
    • node2NameServer2Broker-master2Broker-slave3
    • node3NameServer3Broker-master3Broker-slave1
  • 扩容Topic:可直接扩大Topic下的读写队列数量

缩容方式:

  • 现状:假设机器分布情况如下:
    • node1NameServer1Broker-master1Broker-slave2
    • node2NameServer2Broker-master2Broker-slave3
    • node3NameServer3Broker-master3Broker-slave1
  • 缩容NameServer:减少1台NameServer
    1. 空闲时依次更新生产者和消费者的NameServer配置
    2. 依次更新原Broker的NameServer列表配置
    3. 停止NameServer3
  • 缩容Broker:分别减少1台主Broker和从Broker
    1. 设置下线BrokerBroker-master3Broker-slave3Broker-slave1为只读,不可写
    2. 监控Broker消息积压情况,当Broker-master3入口TPS(InTPS)为0且Diff为0准备下一步
    3. 先后停止Broker-slave3Broker-slave1Broker-master3
    4. 在node2机器上启动从BrokerBroker-slave1
  • 缩容后机器分布
    • node1NameServer1Broker-master1Broker-slave2
    • node2NameServer2Broker-master2Broker-slave1
  • 缩容Topic:先减少写队列数量,待多出来的读队列消息为空时再减少读队列数量

主从Broker设置只读权限差异:

  • 只读权限作用:运维停机前的必要指令,在NameServer路由表中标记为只读
  • Master Broker:生产者不可写入,对消费者仍可见且可读取已写入的消息
  • Slave Broker:触发流量切换,逐渐将消费者迁移到其它Broker,最终等同于不可用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值