目录
一、简介
RocketMQ能够提供更高的吞吐量和更低的延迟,单机吞吐十万级,支持事务消息、顺序消息、批量消息、定时消息、消息回溯等。其功能主要围绕 NameServer、Broker、Producer、Consumer 四大核心组件。
核心特性:
- 高性能
- 单机万级队列:单台 Broker 支持创建超过一万个持久化队列,能够应对极高并发的生产与消费场景;
- 顺序写磁盘:采用 Commit Log 结构,所有消息按顺序写入一个大文件,利用磁盘顺序写入的高效率,实现高吞吐量和低延迟;
- 零拷贝技术:在消息读取过程中采用零拷贝技术,减少数据在内核空间和用户空间的复制操作,提高I/O 效率。
- 高可靠与高可用
- 分布式架构:系统由 NameServer、Broker、Producer、Consumer 四大组件构成,组件均可水平扩展,实现无单点故障;
- 多副本机制:支持主从副本模式,保证消息在Broker节点间的冗余备份,防止数据丢失;
- 故障自动切换:当主节点发生故障时,系统能够自动将流量切换至备选节点,保证服务连续性。
- 消息模型与丰富功能
- 多种消息类型:普通、顺序、事务、批量、定时、消息回溯等;
- 消息顺序性:提供单分区严格顺序消息和全局顺序消息;
- 事务消息:支持分布式事务,通过两阶段提交和回查机制,确保消息发送与数据库操作的最终一致性。
- 易用性与灵活性
- 多种发送与消费模式:Producer 提供同步、异步、单向发送等多种方式;Consumer 支持拉取、推送两种消费模式;
- 丰富的客户端支持:提供 Java、python、Go 等多种语言的 SDK;
- 易于维护与管理:具备完善的监控指标、报警机制、命令行工具及图形化控制台,便于日常运维和故障排查。
二、工作流程
- NameServer 作为 RocketMQ 的大脑,优先启动。它是一个轻量级的服务,负责管理和维护集群的元数据信息,包括 Broker 服务器的地址、Topic 路由信息等。启动 NameServer 实例后,他们之间并不进行数据同步,每个 NameServer 独立存储自己的元数据信息。
- Broker 启动并向 NameServer 注册。它会向所有 NameServer 注册,发送心跳信息,包括 Broker 的地址、服务状态以及它所承载的 Topic 信息。Broker 之间既可以是主 Broker 也可以是从 Broker,主从之间通过同步机制保证数据的一致性。
- 生产者在发送某个主题的消息之前,先从 NameServer 获取 Broker 服务器地址列表(有可能是集群),然后根据负载均衡算法从列表中选择一台 Broker 进行消息发送。
- 生产者连接到 NameServer,获取到当前 Topic 可用的 Broker 信息;
- 生产者根据负载均衡策略选择一个 Broker,通过 Netty 建立连接,并发送消息到 Broker;
- Broker 接收到消息后,会将其存储到 CommitLog,并根据消息到 topic 和 queue 信息,更新相应的 ConsumeQueue;
- 如果是事务消息,生产者还需要完成事务提交或回滚的流程,确保消息的最终一致。
- 消费者在订阅某个主题的消息之前从 NameServer 获取 Broker 服务器地址列表(有可能是集群),订阅规则由 Broker 配置决定。
- 消费者启动时,连接到 NameServer 获取 topic 路由信息,并选择一个 Broker 进行消费;
- 根据消费模式,从 Broker 拉取/接收消息。在集群模式下,消息只会被一个消费者消费;在广播模式下,消息会被所有订阅该 topic 的消费者消费;
- 消费者消费消息后,会根据消费结果(成功/失败)向 Broker 发送 ACK(确认),Broker 根据 ACK 更新消费点位;
- 如果消费失败,消息可能根据配置的重试策略进行重试,或者最终进入死信队列。
三、四大核心组件
四大核心组件为:NameServer、Broker、Producer、Consumer
1、NameServer
NameServer 相当于服务注册中心的角色,管理 Broker,用来保存 Broker 相关元信息并给 Producer 和 Comsumer 查找 Broker 信息。
NamerServer 是无状态的,可横向扩展,节点之间相互无通信。
每个 Broker 在启动的时候都会到 NameServer 注册,Producer 在发送消息前会根据 topic 到 NameServer 获取到 Broker 的路由信息进而和 Broker 取得联系,Consumer 也会定时获取 topic 的路由信息。
NameServer 之所以被称为无状态,是因为它不依赖持久化存储,节点间不进行状态同步,仅在内存中保存由 Broker 等角色主动上报的动态状态信息,且这些信息在 NameServer 重启后不会保留,这种设计简化了系统架构,提升了服务的可伸缩性和容错能力,同时也降低了运维复杂度:
- 内存存储:NameServer 不依赖持久化的存储介质(如硬盘)来保存其管理的元数据,他通过接收 Broker、Producer、Consumer 的定期心跳消息,将集群中的 Broker、Topic、队列分布情况等全部存储在内存中。这意味着 NameServer 在重启后,如果不接受心跳更新,将失去之前维护的所有状态,需重新从各个角色处接收心跳并重建内存中的状态。
- 节点独立:NameServer 节点之间相互独立,他们并不进行任何状态数据的同步。每个 NameServer 都独立处理心跳请求、注册信息和查询请求,对外提供的服务完全基于各自内存中的实时状态。由于没有状态共享或复制机制,任意 NameServer 节点的宕机或加入都不会影响其它节点的正常运行。
- 服务发现与负载均衡:NameServer 主要负责服务发现和路由寻址功能,即为客户端提供 Broker列表、Toptic 与 Broker之间的映射关系等。客户端(Producer、Consumer)根据 NameServer 返回的数据与具体的 Broker 建立连接,进行消息的生产和消费。
- 容错:NameServer 无状态可以很容易的通过增加节点数量来实现水平扩展和高可用。客户端通常配置多个 NameServer 地址列表,当某个 NameServer 不可用时,可以快速切换到其它节点继续获取服务。此外,无状态特性也意味着故障节点恢复后无需复杂的数据恢复过程,只需要重新开始接收心跳更新即可恢复正常服务。
- 高可用:Broker 启动后向所有 NameServer 注册,生产者在发送消息之前先从 NameServer 获取 Broker 的服务器地址列表(消费者一样),然后根据负载均衡算法从列表中选择一台服务器进行消息发送。NameServer 与每台 Broker 服务保持长链接,并间隔 30s 检查 Broker 是否存活,如果检测到 Broker 宕机,则从路由表中将其移除。
2、Broker
消息服务器(Broker)是消息存储中心,用于接收来自 Producer 的消息并存储,Consumer 从这里获取消息并消费。他还存储与消息相关的元数据,包括用户组、消费进度偏移量、队列信息等。
Broker 分为 master 和 slave ,master 既可以写又可以读,slave 不可以写只可以读。
2.1、部署方式
Broker 的部署相对复杂,分为 master 和 slave ,一个 master 可以对应多个 slave,但是一个 slave 只能对应一个 master。master 和 slave 的对应关系通过指定相同的 BrokerName 不同的 BrokerId来定义,BrokerId为0表示 master,非0表示 slave。Broker 集群有四种属方式:
- 单 Master:一个 master broker,一旦重启或宕机整个服务将不可用;
- 多 Master:broker均为 master 没有 slave,配置简单,单个 master 宕机或重启对应用无影响。缺点是单台机器宕机期间,该机器上未被消费的消息在机器恢复之前不可订阅,消息实时性会受影响;
- 多 Master 多 Slave(异步刷盘):每个 master 均有 slave 多对 master-slave,消息采用异步复制方式同步,主备之间有毫秒级延迟。消息丢失不多,实时性不受影响,master 宕机时 slave 可以继续提供消息消费。缺点是异步复制 master 宕机时会丢失极少量数据。
- 多 Master 多 Slave(同步刷盘):多对 master-slave,消息同步双写主备都成功才返回成功。优点是数据与服务都没有单点问题,master宕机时无消息丢失和消息延迟。缺点是相对于异步复制性能会有所影响。
2.2、高可用与负载均衡
Broker 的高可用与负载均衡主要依赖于:NameServer和自动故障转移实现的。
- NameServer:每个 Broker 与 NameServer 集群中的所有节点建立长链接,定时(每隔30s)注册 topic 信息到所有 NameServer,NameServer 定时(每隔10s)扫描所有存活 Broker 的连接,如果 NameServer 超过2分钟都没有收到心跳,则 NameServer 端开与 Broker 的连接。
- 自动故障转移:当主 Broker 故障时,消费者可以从 NameServer 获取新的 Broker 状态信息,自动切换到从 Broker 进行消息消费,确保服务的连续性。
3、生产者(Producer)
3.1、生产者类型
- NormalProducer:标准生产者,用户发送常规消息,不保证消息的顺序性或事务性;
- OrderProducer:顺序生产者,保证同一主题内消息的顺序性。适合那些对消息有严格顺序要求的场景;
- TranscationProducer:事务生产者,用户支持分布式事务,保证消息发送与本地事务操作的原子性。在消息发送前执行预提交操作,并在事务成功或回滚后确认或撤销消息。
3.2、发送方式
- 同步发送:指消息发送方发出数据后会在收到接收方发回响应之后才发送下一个数据包。一般用于重要通知消息,例如重要邮件、营销短信等;
- 异步发送:指发送方发出数据后,不等接收方发回响应,接着发送下一个数据包。一般用于对影响时间敏感的场景,加入一段时间后检测到某个消息发送失败,可选择重新发送;
- 单向发送:指只负责发送消息而不等待服务器回应且没有回调函数。一般用于对可靠性要求不高的场景,例如日志收集。
3.3、生产者组
生产者组(Producer Group)是一类 Producer 的集合,这类 Producer 通常发送一类消息并且发送逻辑一致。从部署结构上看,生产者通过 Producer Group 的名字来标记自己是一个集群。
3.4、启动流程
- 初始化:创建 DefaultMQProducer 实例,设置生产者组名、NameServer 地址等配置;
- 连接 NameServer:生产者会连接到 NameServer 集群,获取 Topic 的路由信息;
- 发送消息:生产者根据路由信息选择合适的 Broker,通过网络通信客户端(Netty)发送消息到 Broker;
- 发送失败:如果消息发送失败,生产者会根据配置的重试策略进行自动重试。
3.5、高可用
Producer 与 NameServer 集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 topic 路由信息,并向提供服务的 master 建立长连接,且定时向 master 发送心跳。
Producer 完全无状态,可集群部署。
Producer 每隔30s(由 ClientConfig 的 pollNameServerInterval)从 NameServer 获取所有 topic 队列的最新情况,这意味着如果 Broker 不可用,Producer 最多30s能够感知,在此期间发往该 Broker 的所有消息都会失败。
Producer 每隔30s(由 ClientConfig 的 hearbeatBrokerInterval决定)向所有关联的 Broker 发送心跳,Broker 每隔10s扫描所有存活的连接,如果 Broker 在2分钟内没有收到心跳数据,则关闭与 Producer 的连接。
4、消费者
消息订阅者,负责从 Topic 接收并消费消息,它从 Broker 拉取消息或者由 Broker 推送消息给消费者,具体是推还是拉取取决于所使用的消费模式。
4.1、消费者类型
- DefaultMQPushConsumer:推模式消费者。这种消费者注册订阅后,Broker 会根据订阅关系主动将消息推送给消费者,适用于大多数需要实时处理消息的场景。推模式下,消费者需要设置消息处理的回调函数,当有新消息到达时,RocketMQ会调用这个函数来处理消息。
- DefaultMQPullConsumer:拉模式消费者。这种消费者需要自己主动从 Broker 拉取消息,相较于 push 模式,pull 模式提供了更多的控制权,比如消费者可以决定何时以及如何拉取消息,适用于需要更多控制逻辑或者对消息消费时机有特殊需求的场景。
4.2、消费者组
消费者组一类 Consumer 的集合名称,这类 Consumer 通常消费同一类消息并且消费逻辑一致,所以将这些 Consumer 分组在一起。消费者与生产者类似,都是将相同角色的分组在一起并名。
RocketMQ中的消息有个特点:同一条消息,只能被某一消费组其中的一台机器消费,但是可以同时被不同的消费组消费。
4.3、高可用
与生产者一样,Consumer 与 NameServer 集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 取 topic 路由信息,并向提供服务的 master、slave 建立长连接,且定时向 master、slave 发送心跳。
Consumer 既可以从 master 订阅消息,也可以从 slave 订阅消息,订阅规则由 Broker 配置决定。
Consumer 每隔30s从 NameServer 获取 topic 的最新队列情况,这意味着 Broker 不可用时,Consumer 最多需要30s才感知。
Consumer 每隔30s(由 ClientConfig 中 heartbeatBrokerInterval 决定)向所有关联的 Broker 发送心跳,Broker 每隔10s扫描所有存活的连接,若某个连接2分钟内没有发送心跳数据,则关闭连接;并向该 Consumer Group 的所有 Consumer 发出通知,Group 内的 Consumer 重新分配队列,然后消费。
当 Consumer 的到 master 宕机通知后,转向 slave 消费,slave 不能保证 master 的消息100%都同步过来了,因此会有少量的消息丢失。但是一旦 master 恢复,未同步过去的消息会被最终消费掉。
四、设计架构
RocketMQ 的设计基于主题的发布与订阅模式(观察者模式),整体设计追求简单与性能第一,主要体现在以下三个方面:
- NameServer 设计简单:集群间不追求强一致追求最终一致性,集群间互相不通信,极大降低了设计的复杂度;
- 高效的IO存储机制:RocketMQ 追求消息发送的高吞吐量,消息存储文件设计为文件组的概念,组内单个文件大小固定,方便引入内存映射机制。所有主题的消息存储基于顺序写,提升写入性能,同时为了兼顾消费与查找,引入了消息消费队列与索引文件;
- 容忍存在的设计缺陷:适当将某些工作放给 MQ 的使用者。消息中间件中有一个难题:如何保证消息一定能被消费且只消费一次。RocketMQ 的设计者给出的解决办法不是解决这个问题,而是退而求其次,只保证消息被消费者消费,允许被重复消费。
1、NameServer 集群
提供轻量级的服务发现及路由,每个 NameServer 记录完整的路由信息,提供相应的读写服务,支持快速扩展。其主要包含两个功能:
- Broker管理:接收来自 Broker 集群的注册请求,提供心跳机制检测 Broker 是否存活;
- 路由管理:每个 NameServer 持有全部有关 Broker 集群和客户度请求队列的路由信息。
2、Broker 集群
通过提供轻量级的 topic 和 queue 机制处理消息存储,同时支持推(push)和拉(pull)两种模型。提供强大的峰值填充和以原始时间顺序累积数千亿条消息的能力。此外还提供灾难恢复,丰富的指标统计数据和警报机制,这些都是传统的消息系统缺乏的。
Broker 有几个重要的子模块:
- 远程处理模块:Broker 入口,处理来自客户端的请求;
- 客户端管理:管理客户端(生产者、消费者),维护消费者的主题订阅;
- 存储服务:提供在物理硬盘上存储和查询消息的简单API;
- HA服务:提供主从 Broker 间数据同步;
- 索引服务:通过指定键为消息建立索引并提供快速消息查询。
3、Producer 集群
消息生产者支持分布式部署,通过多种负载均衡模式向 Broker 集群发送消息。
4、Consumer 集群
消费者也支持 push 和 pull 模型的分布式部署,还支持集群消费和广播消费。
提供了实时的消息订阅机制,可以满足大多数消费者的需求。
5、集群间的交互方式
- Broker Master 和 Broker Slave 是主从结构,会执行数据同步;
- 每个 Broker 与 NameServer 集群中所有节点建立长连接,定时注册 topic 消息到所有 NameServer;
- Producer 与 NameServer 集群中的其中一个节点(随机)建立长连接,定期从 NameServer 获取 topic 路由信息,并与提供 topic 服务的 Broker Master 建立长连接,定时向 Broker 发送心跳;
- Producer 只能将消息发送到 Broker Master,但是 Consumer 同时和 Broker Master 和 Broker Slave 建立长连接,既可以从 Master 订阅消息,也可以从 Slave 订阅消息。
6、RocketMQ的消息
6.1、顺序消息
一种严格按照顺序进行发布和消费的消息类型,要求消息的发布和消费都按照顺序进行,RocketMQ可以保证消息有序。但是这个顺序是有限制的。
- 全局顺序消费:全局顺序消费是指对于一个特定的主题下的所有消息,都能按照生产者发送的顺序被消费者消费。
- 为了实现这一特性 RocketMQ 要求:所有消息必须发送到同一个队列中,这意味着要保证全局顺序,topic 只能配置一个队列。
- 只能有一个消费者实例来消费这个队列中的消息,因为多消费者实例会破坏消息的顺序性。
- 分区顺序消费(局部顺序):分区顺序在消息有序的基础上进行了折衷,允许消息在一定程度上保持顺序,同时又能横向扩展消费能力。
- 根据某个业务逻辑上的 Sharding Key(比如订单ID)进行分区,相同 Sharding Key 的消息会被发送到同一个队列中。消费者可以配置多个实例,并且每个实例通过 MessageQueueSelector(选择器)确保相关消息被同一个消费者实例消费,从而在每个队列内部保持消息的顺序。
- 这种方式在保证了相同分区内的消息顺序的同时,也允许不同分区的消息并行处理,提高了系统的处理能力。
- 在实现顺序消费时,RocketMQ 的消费者需要使用特定的 API 和配置,例如使用 MessageListenerOrderly 接口来实现顺序消费逻辑,确保消息按照预期的顺序被处理。
- 此外,RocketMQ 通过在队列级别加锁等机制来确保同一时刻只有一个消费者线程能够从一个队列中消费消息,以此来维持消息的顺序性。
6.2、消息过滤
消息过滤是指在消息消费时,消费者可以对同一主题下的消息按照规则只消费自己感兴趣的消息。RocketMQ 消息过滤支持在服务端与消费端的消息过滤机制:
- 在 Broker 端过滤:Broker 只将消费者感兴趣的消息发送给消费者;
- 在消费端过滤:消息由消费端自己过滤,缺点是有很多无用的消息会从 Broker 传输到消费端。
6.3、消息存储
消息中间件一个核心的能力是对消息的存储,一般有两个维度的考量:
- 消息堆积能力
- 消息存储性能
RocketMQ 追求消息存储的高性能,引入内存映射机制,所有主题的消息顺序存储在同一个文件中。同时为了避免消息无限在消息存储服务器中累积,引入了消息文件过期机制与文件存储空间报警机制。RocketMQ 的消息存储机制是其高性能、高可靠性的核心之一,主要采用了文件系统存储方式,并设计了精心优化的数据结构来保证消息的高效存储和检索。有以下几个关键组成部分和机制:
- CommitLog:所有消息主体均存储在一个名为 CommitLog 的文件中,无论属于哪个主题。这种设计简化了消息的存储逻辑,提高了写入速度。CommitLog 文件默认大小为1GB,达到上限后会创建新的文件。消息以追加的方式顺序写入,利用了磁盘顺序写入的高效性。这种存储方式确保了高吞吐量和低延迟,因为顺序写磁盘操作远比随机写入更快。
- ConsumeQueue(消费队列):为了加快消息的查询速度,RocketMQ 为每个主题的每个消息队列创建了单独的 ConsumeQueue 文件。ConsumeQueue 存储了 CommitLog 中消息的逻辑偏移量、消息大小、消息在 CommitLog 中的物理偏移量等信息,形成了消息的索引。这样,消费者在消费时不必直接扫描 CommitLog,而是通过 ConsumeQueue 快速定位到消息在 CommitLog 中的位置,提升了消费效率。
- IndexFile(索引文件):RocketMQ 还提供了基于关键字的索引功能,IndexFile 用于存储消息的索引,是的可以通过消息的 Key 快速查找消息。索引文件并非消息存储的必要部分,主要用于支持消息的按关键字查询,提高特定消息检索的速度。
- 刷盘机制:支持同步刷盘和异步刷盘,刷盘机制确保消息在内存中写入后,会根据配置的策略及时持久化到磁盘,避免因异常导致数据丢失。
- 同步刷盘保证了数据的绝对安全,但牺牲了一定的性能;
- 异步刷盘则提供了更高的吞吐量,但数据安全性略低。
- 主从复制:为了进一步增强消息的可靠性,RocketMQ 支持 Broker 的主从复制,即消息不仅存储在主 Broker 上,还会复制到至少一个从 Broker。这样即使主 Broker 发生故障,也能从从 Broker 中恢复消息,确保消息的可靠性。
综上所述,RocketMQ的消息存储机制通过CommitLog、ConsumeQueue、IndexFile等多种文件结构和高效的刷盘策略,结合主从复制,既保证了消息的高性能存储与检索,也确保了消息的高可靠性和数据安全性。
6.4、延迟消息
RocketMQ 支持延迟消息发送,但并非任意时间而是特定的延迟等级。延迟等级共有18个:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h。默认的18个等级对应的时间可以修改,在 broker.conf中修改配置。
- 消息进入 Broker 后,会被存储在 TopicSCHEDULE_TOPIC_XXXX 中,只是在看板上看不到;
- RocketMQ 在启动时,会从 broker.conf 中获取18个等级对应的时间,延迟等级和时间的关系会存放到 DelayLevelTable 中;
- RocketMQ 会开启18个定时任务每隔100ms,从 TopicSCHEDULE_TOPIC_XXXX 判断18个队列里的第一个消息是否可以被投放,如果可以投放,则投放到原本的目的 topic。判断逻辑:存入时间+延迟时间 > 当前时间。
6.5、事务消息
6.6、广播消息
广播消息允许同一 Consumer Group 内的所有 Consumer 都能收到同一条消息,而不是像集群模式那样只由一个 Consumer 消费。
工作机制:
- Producer 发送消息:消息被发送到Topic,RMQ的Broker存储消息;
- Consumer订阅Topic:
- 所有Consumer(同一 Consumer Group)都能收到相同的消息;
- Consumer 不共享消息(和集群模式不同);
- 消费位点存储:
- 广播模式不会存储消费进度(每次Consumer重启后,会重新消费所有消息);
- 不同于集群模式,集群模式的消费进度存储在Broker端,而广播模式存储在本地。
如何确保所有机器都收到并消费?
关键点:
- 所有Consumer必须处于运行状态,否则它们不会收到消息;
- 广播模式不存储消费进度,因此 Consumer 重启后会重新消费消息;
- 要保证消费成功率,可以使用:
- 消费端手动 ack 机制;
- Broker不会等待消费端的ack,仅用于消费端自身的确认机制
- 消费失败时,重试机制;
- 利用 DLQ(死信队列)监控消费失败的消息
- 消费端手动 ack 机制;
- 如何防止某个消费端没有收到消息而丢失消息
- Consumer启动时回溯N条最近的消息
- Consumer使用pull定期拉取消息,避免网络抖动导致consumer丢失消息
- 在broker端开启“冗余发送”,确保消息不会因为网络问题而丢失
- 消费端记录“已消费端消息ID”防止重复消费
五、Docker下安装部署
1、准备镜像
# 拉取rmq镜像
# 最好带着版本号
docker pull apache/rocketmq:4.7.1
# 拉取控制台镜像
docker pull styletang/rocketmq-console-ng:1.0.0
2、挂载目录
# 创建namesrv挂载目录
mkdir -p /docker/rocketmq/data/namesrv/logs /docker/rocketmq/data/namesrv/store
# 创建broker挂载目录
mkdir -p /docker/rocketmq/data/broker/logs /docker/rocketmq/data/broker/store
# 创建broker配置文件
mkdir -p /docker/rocketmq/data/conf
touch broker.conf
# broker 配置
# 所属集群名称,如果节点较多可以配置多个
brokerClusterName = DefaultCluster
#broker名称,master和slave使用相同的名称,表明他们的主从关系
brokerName = broker-a
#0表示Master,大于0表示不同的slave
brokerId = 0
#表示几点做消息删除动作,默认是凌晨4点
deleteWhen = 04
#在磁盘上保留消息的时长,单位是小时
fileReservedTime = 48
#有三个值:SYNC_MASTER,ASYNC_MASTER,SLAVE;同步和异步表示Master和Slave之间同步数据的机制;
brokerRole = ASYNC_MASTER
#刷盘策略,取值为:ASYNC_FLUSH,SYNC_FLUSH表示同步刷盘和异步刷盘;SYNC_FLUSH消息写入磁盘后才返回成功状态,ASYNC_FLUSH不需要;
flushDiskType = ASYNC_FLUSH
# 设置broker节点所在服务器的ip地址,也就是centosOS7的服务ip
# broker暴露的IP:端口,容器环境注意IP的映射
# 外网IP,报漏服务,可外部访问
brokerIP1 = 120.77.19.44
# 磁盘使用达到95%之后,生产者再写入消息会报错 CODE: 14 DESC: service not available now, maybe disk full
diskMaxUsedSpaceRatio=95
3、启动namesvr
docker run -d -p 9876:9876 \
-itd --network=docker-net --ip=172.20.0.11 \
--name rocketmq_nameserver \
--restart=always \
-v /docker/rocketmq/data/namesrv/logs:/root/logs \
-v /docker/rocketmq/data/namesrv/store:/root/store \
-e "MAX_POSSIBLE_HEAP=100000000" \
apache/rocketmq:4.7.1 \
sh mqnamesrv
看到这个启动成功。
4、启动broker
docker run -d \
-itd --network=docker-net --ip=172.20.0.12 \
--restart=always \
--name rocketmq_broker \
--link rocketmq_nameserver:namesrv \
-p 10911:10911 \
-p 10909:10909 \
-v /docker/rocketmq/data/broker/logs:/root/logs \
-v /docker/rocketmq/data/broker/store:/root/store \
-v /docker/rocketmq/data/conf/broker.conf:/opt/rocketmq-4.7.1/conf/broker.conf \
-e "NAMESRV_ADDR=namesrv:9876" \
-e "MAX_POSSIBLE_HEAP=200000000" \
-e "-Xms128m -Xmx128m -Xmn64m" \
apache/rocketmq:4.7.1 sh mqbroker \
-c /opt/rocketmq-4.7.1/conf/broker.conf
看到这个启动成功。
注:namesvr和broker调整JVM内存大小
namesvr和broker用的是同一个镜像,启动后再镜像内可以看到namesvr和broker的启动文件,
修改配置文件参数即可。
5、启动控制面板
docker run -d \
-itd --network=docker-net --ip=172.20.0.13 \
--restart=always \
--name rocketmq_console \
-e "JAVA_OPTS=-Drocketmq.namesrv.addr=172.20.0.11:9876 \
-Dcom.rocketmq.sendMessageWithVIPChannel=false" \
-p 7100:8080 \
styletang/rocketmq-console-ng:1.0.0
看到这个启动成功。
6、访问
六、Q&A
1、RocketMQ会丢消息吗?
RocketMQ作为高性能、高可靠的消息中间件,默认情况下也不能保证绝对不丢消息,但可以通过特定的配置和策略来做到完全不丢失。
RocketMQ丢消息的可能性主要来自以下几个方面:
(1)生产者端丢消息
生产者发送消息但未确认(SEND_OK)
- 生产者发送消息时,RocketMQ需要返回 SEND_OK 才表示消息成功存入 Broker;
- 如果生产者未处理发送失败的情况(比如网络异常,broker宕机),消息可能会丢失
解决方案:
- 使用 syncSend() 并检查 SendResult.getSendStatus(),确保 SEND_OK 才认为发送成功
-
SendResult sendResult = producer.send(msg); if (sendResult.getSendStatus() != SendStatus.SEND_OK) { // 失败时可进行重试或持久化 log.warn("消息发送失败: {}", sendResult); }
- 使用retires机制:
-
producer.setRetryTimesWhenSendFailed(3); // 发送失败重试3次
- 使用事务消息
(2)Broker端丢消息
情况一:Broker异常宕机
- RocketMQ使用异步刷盘(默认),如果Broker崩溃,还没来得及刷盘的数据就会丢失
- 刷盘方式:
- ASYNC_FLUSH(异步刷盘,默认):消息可能丢失,因为 Broker 可能在刷盘前宕机;
- SYNC_FLUSH(同步刷盘):保证消息落盘,但性能下降
解决方案:
- 开启同步刷盘,确保消息写入磁盘:
-
brokerRole=SYNC_MASTER flushDiskType=SYNC_FLUSH
- 增加Broker副本 replicationMode=SYNC,确保主从同步后才确认消息
情况二:主从同步问题(主broker宕机后丢失数据)
- RocketMQ支持主从同步(master-slave),但默认是异步复制(ASYNC_SLAVE)
- 如果master崩溃,而slave还未同步最新数据,消息会丢失
- SYNC_FLUSH只保证消息刷盘,不保证主从同步
解决方案:
- 使用 SYNC_MASTER 复制模式,确保master等slave确认再返回 SEND_OK:
-
brokerRole=SYNC_MASTER
- SYNC_MASTER 可以保证数据同步,但会影响性能
(3)消费者端丢消息
情况一:消息未消费成功但 offset 已提交
- RocketMQ消费者默认是 AT_LEAST_ONCE(至少消费一次),即使消费失败,不会自动回滚 offset
- 如果应用代码异常、消费业务崩溃,消息可能未消费但offset已提交,导致消息丢失
解决方案:
- 使用事务消息
- 使用ACK机制,消费成功后再提交offset
-
consumer.registerMessageListener((List<MessageExt> msgs, ConsumeConcurrentlyContext context) -> { for (MessageExt msg : msgs) { try { // 业务逻辑 processMessage(msg); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } catch (Exception e) { // 失败时,消息不会被确认 return ConsumeConcurrentlyStatus.RECONSUME_LATER; } } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; });
情况二:消息被清理
- RocketMQ默认3天未消费的消息会被删除
- 如果消费者挂掉,3天内未恢复,未消费的消息会被自动清理,导致消息丢失
解决方案:
- 修改 messageDelayLevel 延长消息存储时间