ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RocketMQ高可用集群部署实战:单机到多Master与Dledger模式详解

RocketMQ高可用集群部署实战:单机到多Master与Dledger模式详解 1. 单机模式的瓶颈在哪里先说清楚为什么要搭集群RocketMQ 作为生产环境中大规模使用的消息中间件很多团队一开始都是从单机开始的部署简单、配置少、出问题排查也容易。但只要你把 RocketMQ 真正放到业务流量里跑上几个月就会慢慢发现单机模式的几个硬伤。首先是单点故障。Broker 只有一台一旦这台机器磁盘满了、内存溢出、进程被 OOM Kill或者机房断电整个 Broker 就不可用了。Producer 还能继续发消息但消息会一直重试直到发送超时Consumer 端消息直接拉不到业务链路瞬间卡住。更难受的是如果这台机器物理损坏CommitLog 里的存量消息可能全丢连补偿的机会都没有。其次是容量和性能的天花板。单台 Broker 的写入能力取决于 CPU、内存和磁盘 IO即使你配了 SSD单机写入 TPS 到了某个量级之后很难再往上走。而且 RocketMQ 的消息存储文件是顺序追加写的单机磁盘空间一旦用尽Broker 会直接拒绝写入触发快速失败保护机制。业务量上涨之后你想通过加配置来扛撑不了多久。第三是故障恢复缺乏自动化。单机模式下 Broker 挂了你得手动重启、手动恢复整个过程至少几分钟到几十分钟。对核心链路来说这几分钟就是生产事故。集群能解决的就是这三个核心问题高可用一台挂了有备机接管、水平扩展加 Broker 机器就能扛更多流量、故障转移某台 Master 宕机后消费者还能从其他 Broker 或 Slave 上拉消息业务不中断。所以在决定要不要搭集群、搭哪种集群之前先想清楚一个问题你的 RocketMQ 承载的流量链路能容忍多长时间的不可用容忍时长决定了你要选哪种高可用方案。后面章节我会把几种主流集群模式的差异和部署细节全部拆开讲。2. 集群架构的核心组成NameServer 与 Broker 的协作方式RocketMQ 集群里最核心的两个角色是NameServer和Broker理解它们各自干什么、怎么配合比死记配置项重要得多。2.1 NameServer轻量级路由中心但不像你想象的那样复杂NameServer 的作用是维护 Broker 的路由信息包括 Broker 的地址、存活状态、Topic 配置等。Producer 发消息之前先找 NameServer 拿 Broker 地址Consumer 拉消息之前同样要先问 NameServer 要路由。很多人第一次接触 RocketMQ 集群误以为 NameServer 是像 ZooKeeper 那样的强一致性注册中心。实际不是。NameServer 之间是互相独立的没有数据同步也用不着部署成对等集群来搞选举。它就是一个轻量的、保证最终一致的路由表存储节点。多台 NameServer 部署在一起只是为了不单点Producer 和 Consumer 会同时向所有 NameServer 发起请求谁可用就用谁。我在实际部署中习惯至少部署 2 台 NameServer最好是奇数台比如 3 台。虽然它不选举但奇数台在运维上更规整而且每一台的 broker 路由信息都是全量的即使挂掉一台剩下的还能正常提供服务。NameServer 本身非常轻量内存占用大约几百 MB 到 1 GB 左右建议和 Broker 分离部署避免 Broker 把内存吃满之后把 NameServer 也拖垮。2.2 Broker真正存储消息的地方Master 和 Slave 的分工Broker 才是真正干活的节点消息的存储和读写全在 Broker 上完成。Broker 的角色分为 Master 和 Slave每个 Broker 需要有一个唯一的brokerName同一个 brokerName 下的 Master 和 Slave 构成一个Broker 组。Master 负责处理写入请求和读取请求Slave 负责从 Master 同步数据并在 Master 不可用时承担读取任务。同步方式有两种一种叫同步复制一种叫异步复制两者的差异我放到第三章详细对比这里先记住这个概念。Broker 在启动时会向所有的 NameServer 注册自己的元数据包括 brokerName、brokerId0 表示 Master非 0 表示 Slave、IP 地址、端口号等等。NameServer 拿到这些信息后就为 Producer 和 Consumer 提供路由查询服务。2.3 一条消息从发送到消费集群里发生了什么我用一个完整的流程把集群协作串起来这样你理解起来更直观多个 Broker 启动后各自向 NameServer 注册自己的信息并开启心跳上报。Producer 启动时先从 NameServer 拉取 Topic 的路由信息然后选择一台 Broker 发送消息。Broker 收到消息后将消息顺序写入 CommitLog 文件同时构建 ConsumeQueue 索引并返回写入结果给 Producer。Consumer 启动后同样从 NameServer 拉取路由信息找到目标 Broker主动拉取消息进行消费。如果某台 Broker 宕机NameServer 会在一定时间内默认 10 秒检测不到心跳就会把这个 Broker 的路由信息剔除客户端下次拉取路由时就会避开这台故障 Broker。这里有一个容易踩坑的地方NameServer 剔除故障 Broker 的时间并不是即时的它有一个扫描周期。默认情况下NameServer 每隔 10 秒扫描一次 Broker 列表判断是否有 Broker 长时间没有上报心跳。也就是说一台 Broker 物理宕机之后客户端可能需要 10 到 30 秒才能感知到并切换路由。对于要严格控制故障转移时间的业务来说这个默认值偏长可以通过调整 NameServer 端的scanPeriod和 Broker 端的心跳间隔来缩短感知时间。不过在调参之前你要先想清楚频繁的扫描和心跳检查在高并发场景下会带来额外的网络开销不要盲目追求极致的故障感知速度。3. 四种集群模式逐一拆解从单 Master 到 Dledger 自动选主RocketMQ 官方的集群模式有四种很多新手查资料容易看混我在这里把它们放在一起对比同时把适合的场景说清楚。3.1 单 Master 模式入门可以生产慎用这是最简单的一种部署只有一个 Broker 角色不区分 Master 和 Slave。所有消息都在这一个 Broker 上存储和读写。这种模式的好处是部署极简适合本地开发、功能测试、学习体验。缺点是单点Broker 挂了整个消息链路就断了没有备机可以顶上。数据可靠性也只能依赖这块磁盘磁盘坏了数据就丢了。如果你的业务还处在 POC概念验证阶段可以用单 Master如果已经开始有真实用户流量我不建议你用这个模式扛生产。3.2 多 Master 模式无 SlaveBroker 之间互备这种模式下集群中有多台 Broker每台都是 Master没有 Slave。架构上不存在主从关系每台 Broker 独立承担一部分 Topic 的存储和读写。如果你在集群里配置了多个 brokerName每个 brokerName 下面是单个 Master这就是多 Master 模式。优点没有主从同步的开销写入性能是最高的。某台 Broker 挂了其他 Broker 上的 Topic 还能继续服务不会全集群瘫痪。缺点挂掉的那台 Broker 上的未消费消息在它恢复之前是读不了的消费者只能等 Broker 重启。数据只有一份如果机器损坏消息会丢失。适合对性能要求极高、但对单点故障容忍度较高的场景比如部分日志类数据、非核心异步通知等。说实话在业务量大且核心的生产环境多 Master 模式已经不太够用了。3.3 多 Master 多 Slave 模式生产最常用的配置在多个 Master 的基础上给每个 Master 配一个或多个 Slave就形成了多 Master 多 Slave 模式。消息先写入 MasterMaster 再把数据复制到 Slave。这种模式下每个 Broker 组内部有主从关系Broker 组之间是互相独立、相互备份的。根据复制方式的不同又分为两种异步复制ASYNC_MASTERMaster 写入消息成功后立即向 Producer 返回成功Slave 异步从 Master 拉取数据。这种方式主从有短暂的延迟极端情况下如果 Master 写完之后立刻宕机还没来得及同步的数据会丢。同步复制SYNC_MASTERMaster 必须把消息写入到 Master 和 Slave 都成功之后才向 Producer 确认成功。这种方式数据可靠性大幅提升但写入延迟会变高因为多了一次跨节点的同步等待。在实际生产里如果你的业务对消息不能丢敏感比如交易订单、支付结果通知至少要用同步复制如果只是日志推送、非核心通知异步复制配一个 Slave 也能满足 HA 需求。3.4 Dledger 模式基于 Raft 协议的自动故障切换Dledger 是 RocketMQ 在 4.5 版本之后引入的基于 Raft 协议的存储模式它的核心价值是实现了 Broker 的自动选主和故障切换不需要人工干预。在没有 Dledger 的普通主从模式下Master 挂了Slave 只是能读不能自动变成新的 Master需要你手动去改配置、重启 Broker把 Slave 提升为 Master。整个过程不仅繁琐而且在业务流量大的时候几分钟的人工切换时间里消息收发受到很大影响。Dledger 用 Raft 协议管理 Broker 组里的多台节点节点之间会通过投票选出 Leader相当于 Master其他节点是 Follower相当于 Slave。当 Leader 宕机之后Follower 之间会重新发起选举自动选出新的 Leader整个过程不需要人工介入。Dledger 模式下RocketMQ 数据库引用了一个新的名词RAFFile。实际上它改变的是消息存储层的复制协议对上层业务透明Producer 和 Consumer 无需感知 Leader 切换。部署上一个 Dledger 节点组通常至少需要 3 台 Broker保证选主过半数。如果你的团队运维能力强、追求自动化故障转移Dledger 模式是目前比较理想的选择。模式Master 数量Slave 数量故障转移方式数据可靠性适合场景单 Master1无无低开发测试多 Master2无手动切换低日志、非核心异步多 Master 多 Slave异步2每个 Master 至少 1 个手动切换中普通业务、可容忍少丢多 Master 多 Slave同步2每个 Master 至少 1 个手动切换高订单、支付等核心链路Dledger3 节点组自动选举自动高要求自动故障转移的核心业务4. 多 Master 多 Slave异步复制模式的生产级部署实录这一节我以实际生产环境为例完整走一遍多 Master 多 Slave异步复制的部署过程包含环境规划、配置参数、启动顺序和验证手段。这套方案在大多数业务场景下足够用也是我目前最推荐中小团队上手的配置。4.1 服务器规划与前置条件以一个 2 Master 2 Slave 的最小高可用集群为例你需要 4 台机器节点角色IP 示例配置建议nameserver-1NameServer10.0.0.112C4G内存建议 4Gnameserver-2NameServer10.0.0.122C4G内存建议 4Gbroker-a-masterMasterbroker-a10.0.1.114C8G磁盘 100G SSDbroker-a-slaveSlavebroker-a10.0.1.124C8G磁盘 100G SSDbroker-b-masterMasterbroker-b10.0.1.134C8G磁盘 100G SSDbroker-b-slaveSlavebroker-b10.0.1.144C8G磁盘 100G SSD如果你机器资源紧张NameServer 和 Broker 可以先合部署但生产环境我强烈建议分开尤其是 Broker 的磁盘 IO 压力大不应影响 NameServer 的心跳和路由管理。操作系统建议 CentOS 7.x 或 Ubuntu 20.04JDK 必须用64 位 JDK 1.8 以上推荐 OpenJDK 1.8 或 11。RocketMQ 4.9.x 系列对 JDK 1.8 兼容性最稳如果你用的版本在 5.0 以上建议配合 JDK 11 使用避免某些新特性在旧 JDK 上出现兼容问题。4.2 核心配置文件详解RocketMQ 的 Broker 配置文件默认放在conf/目录下不同模式对应不同模板。在这里我直接用命令行参数指定方式但为了好维护还是建议你把配置写到文件里然后通过-c参数指定。以一个 Master 的配置为例文件broker-a.properties# 集群名称所有 Broker 必须一致 brokerClusterNameDefaultCluster # 这个 Broker 组的名称同一个组的主从必须一致 brokerNamebroker-a # 0 表示 Master非 0 表示 Slave brokerId0 # NameServer 地址多个之间用分号隔开 namesrvAddr10.0.0.11:9876;10.0.0.12:9876 # 消息存储路径确保目录存在且有权限 storePathRootDir/data/rocketmq/store/broker-a-master storePathCommitLog/data/rocketmq/store/broker-a-master/commitlog # Broker 对外提供服务的 IP brokerIP110.0.1.11 # 监听端口 listenPort10911 # 自动创建 Topic 开关生产环境建议 true autoCreateTopicEnabletrue # 自动创建消费组开关 autoCreateSubscriptionGrouptrue # 文件刷盘方式同步刷盘更安全 flushDiskTypeSYNC_FLUSH # 主从复制方式这里是异步复制 brokerRoleASYNC_MASTER对应的 Slave 配置broker-a-s.properties# 同一个集群 brokerClusterNameDefaultCluster # 同一个 Broker 组 brokerNamebroker-a # Slave非 0 即可通常用 1 brokerId1 namesrvAddr10.0.0.11:9876;10.0.0.12:9876 storePathRootDir/data/rocketmq/store/broker-a-slave storePathCommitLog/data/rocketmq/store/broker-a-slave/commitlog brokerIP110.0.1.12 listenPort10911 autoCreateTopicEnabletrue autoCreateSubscriptionGrouptrue flushDiskTypeSYNC_FLUSH # Slave 的角色 brokerRoleSLAVEbroker-b 的配置就是把 brokerName 改成 broker-bbrokerId 0/1IP 改成 10.0.1.13 和 10.0.1.14 即可。注意每个 Broker 的存储路径要独立不能共用一个目录。这里有几个关键的配置点需要说明brokerClusterName 必须一致否则不同机器之间会被认为是不同的集群路由信息会乱。同一个 brokerName 下的主从brokerId 是唯一的Master 固定为 0Slave 从 1 开始递增。brokerIP1 要写对特别是在多网卡环境中如果不显式指定RocketMQ 会自动识别一个内网 IP可能导致生产客户端无法连通。flushDiskType 和 brokerRole 根据你的可靠性要求来调生产核心业务建议 SYNC_FLUSH能防掉电丢消息。4.3 启动顺序与常用命令整个启动顺序有个基本原则先启 NameServer再启 Broker。Broker 启动时需要向 NameServer 注册所以 NameServer 必须保证可用。启动 NameServer# 在 10.0.0.11 和 10.0.0.12 上执行 nohup sh mqnamesrv /data/rocketmq/logs/namesrv.log 21 确认 NameServer 启动成功查看日志tail -f /data/rocketmq/logs/namesrv.log # 看到 The Name Server boot success 代表成功启动 Masterbroker-a 的 Master 节点nohup sh mqbroker -c /data/rocketmq/conf/broker-a.properties /data/rocketmq/logs/broker-a-master.log 21 启动 Slavebroker-a 的 Slave 节点nohup sh mqbroker -c /data/rocketmq/conf/broker-a-s.properties /data/rocketmq/logs/broker-a-slave.log 21 启动 broker-b 的 Master 和 Slave步骤同上换成对应配置文件即可。查看集群状态cd /data/rocketmq/bin sh mqadmin clusterList -n 10.0.0.11:9876输出格式类似这样#Cluster Name #Broker Name #BID #Addr #Version DefaultCluster broker-a 0 10.0.1.11:10911 V4_9_4 DefaultCluster broker-a 1 10.0.1.12:10911 V4_9_4 DefaultCluster broker-b 0 10.0.1.13:10911 V4_9_4 DefaultCluster broker-b 1 10.0.1.14:10911 V4_9_4BID 为 0 的是 Master非 0 的是 Slave。看到这套信息说明你的 Multi Master 集群已经正常工作了。4.4 部署 Dashboard 监控控制台RocketMQ 官方提供了一个 Web 控制台项目叫 RocketMQ Dashboard可以查看集群状态、Topic 数据、消费进度和消息轨迹。部署方式有几种我常用的是用 Docker 跑一份简单省事docker run -d \ --name rocketmq-dashboard \ -e NAMESRV_ADDR10.0.0.11:9876;10.0.0.12:9876 \ -e JAVA_OPTS-Drocketmq.namesrv.addr10.0.0.11:9876;10.0.0.12:9876 \ -p 8080:8080 \ apacherocketmq/rocketmq-dashboard:latest登录界面之后可以重点查看这几个页面集群页面确认所有 Broker 都在线主从关系正确。Topic 页面查看主题的路由分布是否均匀。消费者页面检查消费组是否有堆积、延迟情况。Dashboard 本身不参与消息链路挂了不影响业务但建议至少部署一份用于日常巡检。生产环境把 Dashboard 的端口限制在内网不要直接暴露公网。5. 部署验证与常见故障排查实操集群部署起来只是第一步真正考验人的是后续的验证和排障。这一节我把我在实践中遇到的典型问题和对应排查思路完整列出来。5.1 集群是否真的高可用做个故障演练最直接的办法就是主动 kill 某一个 Master 进程然后观察生产端的表现# 在 broker-a-master 机器上执行 kill -9 $(pidof java)然后立即用 mqadmin 查看集群状态sh mqadmin clusterList -n 10.0.0.11:9876你能看到 broker-a 的 Master 已经不在列表里但 Slave 还在。此时如果消费者有配置 slaveReadEnable默认在 4.9.x 后的版本需要显式打开老版本默认可以从 Slave 读消息读取可以从 Slave 继续否则消费者会一直尝试连接 Master直到 Master 恢复。这就是为什么我建议你在 consumer 的相关配置里把slaveReadEnable设为 true至少在运维层面留一条冗余读路径consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); consumer.setMessageModel(MessageModel.CLUSTERING);实际上这是 JVM 参数层面的调整在 client 端没有太直接的开关更常见的做法是配置 Broker 端的slaveReadEnabletrue在 broker 配置文件中设置默认在较新版本中已开启。这一点比较容易在文档里被忽略但故障场景下很有用。5.2 客户端连不上集群的排查链路这是新手最容易遇到的问题现象是 Producer 报connect to 10.0.1.11:10911 failed。排查顺序我建议按照下面的链路走先确认 NameServer 还能不能 ping 通telnet 10.0.0.11 9876检查端口连通性。再确认 Broker 进程是否存活jps看有没有 BrokerStartup 进程。查看 Broker 日志如果发现register broker to name server failed基本是 NameServer 地址配置错误或者网络不通。检查 Broker 的brokerIP1是否配置成客户端可达的 IP。这在云环境尤其是多网卡场景特别常见Broker 默认会取第一块网卡的 IP客户端根本访问不到。再看防火墙和安全组生产环境经常是安全组里没有放开 10911 端口客户端永远连不上。最后的兜底手段用mqadmin sendMessage命令直接向某个 Topic 发一条测试消息能发通就说明链路是通的能大幅缩小排查范围。sh mqadmin sendMessage -n 10.0.0.11:9876 -t TestTopic -p hello message如果命令报错错误信息会直接指出是 NameServer 不可达还是 Broker 不存在还是路由没有。5.3 自动创建 Topic 的隐藏坑VIP 通道有个非常经典的问题RocketMQ 发送消息时默认会走一个 VIP 通道端口是 Broker 端口基础上减 2。比如 Broker 监听 10911客户端默认会尝试连 10909。如果你没有在安全组里开放 10909 端口就会出现一个诡异的现象——用命令行工具测试可以但用 java client 发送一直超时。解决办法在客户端指定producer.setSendLatencyFaultEnable(false);或者把 broker 配置中的brokerVIPSupportEnable设为 false。很多生产事故的排查最终都绕回到这个点上建议你在部署文档里直接写清楚安全组需要放行 Broker 监听端口、VIP 端口监听端口减 2、以及 NameServer 的 9876 端口。5.4 主从切换的正确操作姿势Dledger 模式自动选举不需要你操心但普通多 Master 多 Slave 模式下Master 挂了之后你要手动把 Slave 提升为新的 Master。具体操作方式网上说法很多最稳妥的做法是停止这台 Slave 的 Broker 进程。修改配置把 brokerId 改为 0brokerRole 改为 ASYNC_MASTER。使用新的配置重启 Broker。这里仅提供一种可行的方式实际操作中涉及的数据补救和消息追平逻辑根据你的实际架构和版本需要做充分测试。Dledger 模式之所以受欢迎能省掉这些繁琐的人工步骤是一个很大原因。6. 生产环境选型建议你的业务到底适合哪种集群网上关于 Kafka、RabbitMQ、RocketMQ 的选型对比很多这里我不重复单从 RocketMQ 三种主要集群模式的选型角度给你一些实际建议。6.1 中小团队、业务初期、单机房建议配置2 个 NameServer 2 Master 2 Slave异步复制。这个配置成本不高机器大约 4 到 6 台能扛住每秒几千条的写入架构清晰出问题手动切换也能接受。如果你的业务还没到大规模用户量先不要急着上 Dledger因为 Dledger 的运维门槛和维护成本更高团队如果对 Raft 不熟悉遇到问题反而更难排查。6.2 核心交易链路、消息不能丢建议配置2 个 NameServer 2 Master 2 Slave同步复制 Dashboard 监控。同步复制确保 Master 和 Slave 都写成功才返回消息基本不丢。刷盘也要配合使用 SYNC_FLUSH这是磁盘存储层的最后一道保险。在压测阶段要重点观察同步复制带来的延迟变化RocketMQ 同步复制通常能控制在毫秒级但网络抖动会放大延迟所以集群机器之间尽量走同机房低延时网络。6.3 要求自动故障转移、运维人力有限建议配置2 个 NameServer 3 节点 Dledger 组。这是我个人对运维团队规模较小、但业务重要性高的团队比较推荐的方案。自动选主可以避免人工切换的滞后和误操作而且 Dledger 模式下客户端不需要感知 Master 切换对业务无侵入。代价是存储和网络开销比普通主从模式大一些3 个节点中最少也要多数派2 个正常工作才能选主所以挂 1 台没事挂 2 台就不可用了。6.4 多机房容灾如果你的业务有多机房需求简单的 RocketMQ 集群不够还需要考虑跨机房的消息同步。常见方案是独立机房各自部署一套集群通过 MirrorMaker 等同步工具做消息复制或者在上层业务做双写。这个话题展开讲又是一篇长篇但核心思路是集群内高可用、集群间数据双活两者是不同层次的问题不要混在一起设计。7. 部署踩坑后的几点实在总结最后分享几个我实际部署 RocketMQ 集群以来的真实感受希望对你有用。第一配置文件的命名和路径一定要规范。broker-a.properties、broker-b.properties不要用 test1、test2 这种名字否则几个月之后你根本分不清哪个 Broker 在跑哪份配置尤其是做故障切换的时候简直灾难。第二日志和存储路径要提前规划好。RocketMQ 的日志默认会输出到启动目录下我见过不少团队因为没指定-Drocketmq.client.logRoot和 ROCKETMQ_HOME导致日志散落各处最后排查问题无从下手。建议所有 Broker 统一用一个日志目录并在部署脚本里固定。第三启动之前用ulimit -n把文件句柄数调大。RocketMQ 的 Broker 会打开大量文件默认的 1024 限制在生产环境必炸。在/etc/security/limits.conf里给启动用户加一句* soft nofile 655350重启进程后ulimit -n确认生效。第四JVM 参数不要直接跑默认值。RocketMQ 官方脚本默认的堆内存可能偏大或偏小要根据你的机器规格调整。我一般建议 Broker 进程堆内存设置在 4G 到 8G 左右同时留足堆外内存给 PageCache因为 RocketMQ 大量依赖操作系统的页缓存来做消息读写加速。JVM 配置不合理内存和性能都会出问题。第五消费堆积一定要有指标告警。集群搭得再好如果消费速度跟不上生产速度消息堆积会拖垮整个链路。Dashboard 里的消费者页面能看到堆积量但建议你在监控系统里对ConsumerLag设置阈值告警达到阈值就提醒不要等业务方反馈才去查。RocketMQ 集群部署本身并不复杂复杂的是对架构原理的理解和故障场景下的快速定位能力。希望这篇文章能帮你把集群的骨架搭起来并且知道出了问题该往哪查。如果你的业务量还在起步阶段完全可以先用多 Master 多 Slave 模式跑起来等确实需要自动切换了再平滑演进到 Dledger 模式这个升级路径 RocketMQ 是支持的不必一开始就把所有高可用方案全部铺上。
返回列表