RocketMQ 启动过程分析&大流量场景优化

瞬时大流量场景 MQ 消费的优化项

  • 增加 mq 消息分区数,保证最大并发度

  • 增加消费组内的消费实例数:消费者数>=topic分区数

  • 合理设置 mq 消费线程数:ConsumeThreadMin、ConsumeThreadMax,两个值应该相等,默认 20

  • 顺序消息阻塞:业务可以在return消费结果之前增加日志打点,排查验证,解决阻塞代码即可恢复。

Spring自动注册流程

完整流程

这里的流程分析主要基于 rocketmq-spring-boot-starter:2.2.3。
注意:下面列表的缩进关系代表着代码中的调用层级

RocketMQ 声明了 RocketMQAutoConfiguration 的自动配置类,这个类会导入 ListenerContainerConfiguration 配置类,用于创建 Listener 容器。

ListenerContainerConfiguration 在整个容器实例化完成后,会扫描所有带有@RocketMQMessageListener注解的Bean,并针对每一个 bean 注册容器(registerContainer)

  1. CHECK:Listener不能同时实现RocketMQListener接口和RocketMQReplyListener接口,也不能同时不实现RocketMQListener接口和RocketMQReplyListener接口

  2. genericApplicationContext.registerBean("DefaultRocketMQListenerContainer_" + counter, createRocketMQListenerContainer);。这里会实际创建RocketMQListenerContainer,方法实现的内容如下:

    1. 实例化DefaultRocketMQListenerContainer并记录注解及其他相关信息,大多数字段都用了resolvePlaceholders解析,支持${}
    2. 根据 Listener 是否有返回值(RocketMQListener、RocketMQReplyListener),赋值 container 中的 RocketMQListener字段值
    3. DefaultRocketMQListenerContainer 在 Bean 初始化完成后(afterPropertiesSet),会初始化 MQPushConsumer
      1. 如果@RocketMQMessageListener注解配置了 AK 和 CK ,则创建一个 RPCHook,用于创建DefaultMQPushConsumer ,否则,创建一个不指定RPCHook的DefaultMQPushConsumer。
        1. DefaultMQPushConsumer实例化的时候会根据DefaultMQPushConsumer中的参数创建 defaultMQPushConsumerImpl = new DefaultMQPushConsumerImpl(this, rpcHook);
        2. 如果 Consumer 注解上配置了消息追踪(enableMsgTrace),则初始化一个AsyncTraceDispatcher,并注册到 defaultMQPushConsumerImpl
      2. 初始化consumer的所有参数,包括 consumeThreadMax、consumeTimeout、consumeMode、selectorType 等等
      3. 调用Hock方法 RocketMQConsumerLifecycleListener#prepareStart,可以覆盖前面初始化的参数值,比如 consumeThreadMax、pullBatchSize
  3. rocketMQListenerContainer.start();

    1. DefaultMQPushConsumer.start() Part1 => setContainerGroup(namespace, resourceWithOutNamespace)

    2. DefaultMQPushConsumer.start() Part2 => defaultMQPushConsumerImpl.start():具体的实现逻辑很多,抽取我认为重要的:

      1. 默认情况下,广播模式的消息 offset 存储在本地文件(LocalFileOffsetStore),集群模式的 offset 存储在远程 broker(RemoteBrokerOffsetStore)

      2. 如果是顺序消费,consumeMessageService = new ConsumeMessageOrderlyService(),如果是并发消费,consumeMessageService = new ConsumeMessageConcurrentlyService(),实例化时会创建消费用的线程池,线程数使用指定的线程数,BlockingQueue使用 LinkedBlockingQueue。

        • 实例化完成后会调用 start()方法,用于定时清理过期消息

        • 线程池consumeExecutor用于pullAPIWrapper拉取消息成功后,将拉取到的消息交给消费服务进行业务处理

      3. 将 DefaultMQPushConsumer 按消费组的维度注册到 MQClientInstance,MQClientInstance启动方法 start 内部又会执行一系列的start

        • mQClientAPIImpl.start():为RocketMQ客户端建立网络通信基础,处理消息的收发和连接管理。
        • this.startScheduledTask():每 2 分钟获取NameServer地址;每 30 秒更新 Topic 配置;每 30 秒清理离线Broker并发送心跳;每 5 秒持久化所有消费者offset;每分钟调整一次DefaultMQPushConsumerImpl线程池配置
        • this.pullMessageService.start():启动异步线程 PullMessageService#run ,主要处理消费者 DefaultMQPushConsumerImpl#executePullRequestLater(timeDelay) 添加的消息请求,根据消息请求模式(PULL、POP)决定消息消费模式
          • 如果是推送消息:异步调用Broker的POP接口获取消息,通过回调处理拉取结果
          • 如果是拉取消息:执行流控检查(消息堆积、内存占用、消息跨度);顺序消费时检查队列锁状态;调用pullKernelImpl向Broker发起拉取请求。 实际底层仍是主动拉取,通过流控和回调机制模拟了推送效果。
        • this.rebalanceService.start():启动异步线程,等待RebalanceService启动完成后,对每个consumerGroup 对应的消费者执行一次rebalance操作
        • defaultMQProducer.getDefaultMQProducerImpl().start():启动生产者实例,内容较多,暂时忽略
    3. DefaultMQPushConsumer.start() Part3 => 如果enableMsgTrace,traceDispatcher.start()

    4. 更新运行状态 DefaultMQPushConsumer.this.setRunning(true)

复制代码
    pullBatchSize   todo

org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#pullMessage

整体流程时序图(不关注细节)

sequenceDiagram participant RocketMQAutoConfiguration participant ListenerContainerConfig participant DefaultRContainer participant DefaultMQPushConsumer participant DefaultMQPushConsumerImpl participant MQClientInstance RocketMQAutoConfiguration->>ListenerContainerConfig: 导入配置类 loop 扫描@RocketMQMessageListener ListenerContainerConfig->>ListenerContainerConfig: registerContainer() ListenerContainerConfig->>DefaultRContainer: registerBean() end DefaultRContainer->>DefaultRContainer: 初始化容器参数 DefaultRContainer->>DefaultMQPushConsumer: 创建消费者实例 DefaultMQPushConsumer->>DefaultMQPushConsumerImpl: 初始化实现类 alt enableMsgTrace DefaultMQPushConsumer->>DefaultMQPushConsumerImpl: 注册TraceDispatcher end DefaultRContainer->>DefaultMQPushConsumer: 调用prepareStart钩子 DefaultRContainer->>DefaultMQPushConsumer: container.start() DefaultMQPushConsumer->>DefaultMQPushConsumer: setContainerGroup() DefaultMQPushConsumer->>DefaultMQPushConsumerImpl: start() DefaultMQPushConsumerImpl->>DefaultMQPushConsumerImpl: 初始化OffsetStore DefaultMQPushConsumerImpl->>DefaultMQPushConsumerImpl: 创建消费服务 DefaultMQPushConsumerImpl->>MQClientInstance: 注册并启动 MQClientInstance->>MQClientInstance: 启动核心服务: MQClientInstance-->>MQClientInstance: 1. 网络通信(mQClientAPIImpl) MQClientInstance-->>MQClientInstance: 2. 定时任务(startScheduledTask) MQClientInstance-->>MQClientInstance: 3. 消息拉取服务(pullMessageService) MQClientInstance-->>MQClientInstance: 4. 负载均衡服务(rebalanceService) MQClientInstance-->>MQClientInstance: 5. 生产者服务(defaultMQProducer) alt enableMsgTrace DefaultMQPushConsumer->>DefaultMQPushConsumerImpl: traceDispatcher.start() end DefaultMQPushConsumer->>DefaultMQPushConsumer: setRunning(true)

完整流程时序图

sequenceDiagram participant RocketMQAutoConfiguration as RocketMQAutoConfiguration participant ListenerContainerConfig as ListenerContainerConfiguration participant DefaultRContainer as DefaultRocketMQListenerContainer participant DefaultMQPushConsumer as DefaultMQPushConsumer participant DefaultMQPushConsumerImpl as DefaultMQPushConsumerImpl participant MQClientInstance as MQClientInstance participant ConsumeMsgService as ConsumeMessageService participant TraceDispatcher as AsyncTraceDispatcher participant RocketMQLifecycle as RocketMQConsumerLifecycleListener %% Spring自动配置阶段 RocketMQAutoConfiguration->>ListenerContainerConfig: @Import ListenerContainerConfiguration %% 监听器扫描和容器注册 loop 扫描所有@RocketMQMessageListener Bean activate ListenerContainerConfig ListenerContainerConfig->>ListenerContainerConfig: registerContainer() ListenerContainerConfig->>ListenerContainerConfig: CHECK接口实现
(不能同时/不实现两个接口) ListenerContainerConfig->>DefaultRContainer: registerBean("DefaultRocketMQListenerContainer_"+counter) deactivate ListenerContainerConfig end %% 容器初始化阶段 activate DefaultRContainer DefaultRContainer->>DefaultRContainer: 1. 实例化并记录注解信息
(resolvePlaceholders解析${}) DefaultRContainer->>DefaultRContainer: 2. 根据Listener返回值类型
设置RocketMQListener字段 %% MQPushConsumer初始化 DefaultRContainer->>DefaultMQPushConsumer: 3. 初始化MQPushConsumer
(afterPropertiesSet) activate DefaultMQPushConsumer %% RPCHook和TraceDispatcher创建 alt 注解配置AK/CK DefaultMQPushConsumer->>DefaultMQPushConsumer: 创建RPCHook else 未配置AK/CK DefaultMQPushConsumer->>DefaultMQPushConsumer: 不指定RPCHook end DefaultMQPushConsumer->>DefaultMQPushConsumerImpl: 创建实例(传入RPCHook) activate DefaultMQPushConsumerImpl alt enableMsgTrace=true DefaultMQPushConsumer->>TraceDispatcher: 创建并注册 activate TraceDispatcher end %% 参数初始化和钩子调用 DefaultMQPushConsumer->>DefaultMQPushConsumer: 初始化参数:
consumeThreadMax, consumeTimeout,
consumeMode, selectorType等 DefaultRContainer->>RocketMQLifecycle: prepareStart()钩子 activate RocketMQLifecycle RocketMQLifecycle->>DefaultMQPushConsumer: 可覆盖参数如
consumeThreadMax, pullBatchSize deactivate RocketMQLifecycle %% 消费者启动流程 DefaultRContainer->>DefaultMQPushConsumer: container.start() %% Part 1: 设置组名 DefaultMQPushConsumer->>DefaultMQPushConsumer: Part1: setContainerGroup() %% Part 2: DefaultMQPushConsumerImpl启动 DefaultMQPushConsumer->>DefaultMQPushConsumerImpl: Part2: start() %% Offset存储选择 alt 广播模式 DefaultMQPushConsumerImpl->>DefaultMQPushConsumerImpl: LocalFileOffsetStore else 集群模式 DefaultMQPushConsumerImpl->>DefaultMQPushConsumerImpl: RemoteBrokerOffsetStore end %% 消费服务创建 alt 顺序消费 DefaultMQPushConsumerImpl->>ConsumeMsgService: 创建ConsumeMessageOrderlyService else 并发消费 DefaultMQPushConsumerImpl->>ConsumeMsgService: 创建ConsumeMessageConcurrentlyService end activate ConsumeMsgService ConsumeMsgService->>ConsumeMsgService: 创建线程池
(指定线程数+LinkedBlockingQueue) ConsumeMsgService->>ConsumeMsgService: start()定时清理过期消息 deactivate ConsumeMsgService %% MQClientInstance启动 DefaultMQPushConsumerImpl->>MQClientInstance: 注册并启动 activate MQClientInstance MQClientInstance->>MQClientInstance: mQClientAPIImpl.start()
建立网络通信 MQClientInstance->>MQClientInstance: startScheduledTask()启动定时任务:
- 每2分钟获取NameServer
- 每30秒更新Topic配置
- 每30秒清理离线Broker+心跳
- 每5秒持久化offset
- 每分钟调整线程池 MQClientInstance->>MQClientInstance: pullMessageService.start() %% 拉取服务处理逻辑 alt 推送模式(PUSH) MQClientInstance->>MQClientInstance: 异步POP接口+回调处理 else 拉取模式(PULL) MQClientInstance->>MQClientInstance: 流控检查(堆积/内存/跨度)
顺序消费检查队列锁
pullKernelImpl向Broker拉取 end MQClientInstance->>MQClientInstance: rebalanceService.start()
启动后立即执行rebalance MQClientInstance->>MQClientInstance: defaultMQProducer启动
(细节略) deactivate MQClientInstance deactivate DefaultMQPushConsumerImpl %% Part 3: TraceDispatcher启动 alt enableMsgTrace=true DefaultMQPushConsumer->>TraceDispatcher: Part3: TraceDispatcher.start() deactivate TraceDispatcher end %% 状态更新 DefaultMQPushConsumer->>DefaultMQPushConsumer: this.setRunning(true) deactivate DefaultMQPushConsumer deactivate DefaultRContainer


基于 AI 生成的文章

来尝试一下 AI 吧!下面是基于 markdown 的 AI 生成完整文章的提示词,部分内容或许有错误,重点关注AI提示词和生成效果

markdown 复制代码
我是一个java 后端开发,想做一次关于 "rocketmq 在 spring 容器启动过程中的具体实现"的技术分享,你根据下面我整理的内容帮我写一份分享,要求:

- 语言轻松,避免过于死板
- 分享的内容遵循"总-分-总"的结构,避免平铺直叙
- 分享内容的层级结构清晰,容易理解
- 注意我整理内容的层级结构,我的内容反应了方法间调用的层级关系
- 可以穿插时序图或流程图,展现形式越丰富越好,穿插的内容必须和上下文强相关,并且确保内容的正确性!
- 可以适当参考其他资料,但是必须以我提供的内容为主

🌟 揭秘RocketMQ在Spring容器启动中的魔法实现

------从注解到消息消费的全链路解析

关键词:@RocketMQMessageListener、自动装配、消费者启动、线程池管理


一、总览:Spring容器启动时的RocketMQ自动化装配

graph LR A([Spring启动])-->B[RocketMQAutoConfiguration] B-->C[ListenerContainerConfiguration] C-->D{扫描RocketMQMessageListener注解} D-->E[为每个注解创建容器]

当Spring容器启动时,RocketMQAutoConfiguration这个自动配置类 会导入关键配置ListenerContainerConfiguration。它在容器就绪后执行一个重要任务:扫描所有带@RocketMQMessageListener注解的Bean,并创建对应的消息监听容器。

本质:把简单的注解声明 ➡️ 转化为可运行的RocketMQ消费者


二、详解:三大核心实现阶段

▶️ 阶段1:容器注册(registerContainer)
java 复制代码
// 伪代码示例:容器注册本质
for (Bean bean : allBeans) {
    if (has @RocketMQMessageListener) {
        checkListenerType(bean); // 合规检查
        createContainer(bean);   // 容器创建
    }
}
🔍 关键动作:
  1. 合规检查(重要!)

    • ❌ 禁止:同时实现RocketMQListenerRocketMQReplyListener
    • ❌ 禁止:两个接口都不实现
    • ✅ 要求:必须且仅实现其中一个
  2. 动态创建容器

    scss 复制代码
    // 实际创建逻辑
    genericAppCtx.registerBean(
      "DefaultRocketMQListenerContainer_"+counter, 
      () -> createRocketMQListenerContainer(anno, bean)
    );
    • 解析注解参数(支持${}占位符)
    • 绑定Listener实现类(根据有无返回值区分类型)

▶️ 阶段2:容器初始化(afterPropertiesSet)
sequenceDiagram Container->>DefaultMQPushConsumer: 实例化 DefaultMQPushConsumer->>DefaultMQPushConsumerImpl: 构造核心实现类 alt enableMsgTrace=true DefaultMQPushConsumerImpl->>AsyncTraceDispatcher: 创建消息追踪器 end
🔧 核心步骤:
  1. 消费者实例化

    • 根据AK/SK配置创建RPCHook
    • 构建DefaultMQPushConsumerImpl核心组件
  2. 关键参数初始化

    • consumeThreadMax(最大消费线程数)
    • consumeMode(消费模式:并发/顺序)
    • selectorType(消息过滤机制)
  3. 生命周期钩子

    scss 复制代码
    // 参数覆盖示例
    RocketMQConsumerLifecycleListener.prepareStart(consumer);
    • 可通过Hook覆盖初始化参数(如动态调整pullBatchSize)

▶️ 阶段3:容器启动(container.start())

🚀 Part1: 基础配置

  • 设置ConsumerGroup(添加namespace防冲突)

🚀 Part2: 核心启动流程

flowchart TB A[consumer.start] --> B[初始化OffsetStore] B --> C{广播模式} C -->|是| D[LocalFileOffsetStore] C -->|否| E[RemoteBrokerOffsetStore] A --> F[创建消费服务] F --> G{顺序消费} G -->|是| H[ConsumeMessageOrderlyService] G -->|否| I[ConsumeMessageConcurrentlyService] A --> J[注册MQClientInstance] J --> K[启动网络通信] J --> L[启动定时任务] J --> M[启动拉消息服务] J --> N[启动重平衡服务]

关键实现:

  1. 消费服务创建

    • 顺序消费:ConsumeMessageOrderlyService
    • 并发消费:ConsumeMessageConcurrentlyService
    • ⚡️ 初始化消费线程池(核心配置参数:consumeThreadMax
  2. MQClientInstance启动

    • 周期性任务:
      • 30秒/次:更新Topic路由信息
      • 5秒/次:持久化消费位点
      • 30秒/次:向Broker发送心跳
    arduino 复制代码
    // 线程池初始化示例
    consumeExecutor = new ThreadPoolExecutor(
      this.defaultMQPushConsumer.getConsumeThreadMin(),
      this.defaultMQPushConsumer.getConsumeThreadMax(),
      1000 * 60,
      TimeUnit.MILLISECONDS,
      new LinkedBlockingQueue<>(queueSize)
    );
  3. 消息拉取服务

    • PullMessageService核心逻辑:

      scss 复制代码
      // 简化的轮询逻辑
      while (!stopped) {
          PullRequest request = pullRequestQueue.take(); 
          pullMessage(request); // 触发实际拉取
      }
    • 流控策略:检查消息堆积/内存占用/队列锁状态


🚀 Part3:收尾工作

  • 启动消息轨迹跟踪(如配置enableMsgTrace)
  • 更新容器状态:running=true

三、总结:RocketMQ+Spring协作全景图

flowchart LR 1[自动装配] --> 2[注解扫描] 2 --> 3[容器注册] 3 --> 4[消费者初始化] 4 --> 5[线程池创建] 5 --> 6[网络层启动] 6 --> 7[消息拉取] 7 --> 8[消息消费]
💡 避坑指南:
  1. 广播模式offset存本地,重启需考虑位点同步问题
  2. 顺序消费线程数设置过大会导致文件锁竞争
  3. TraceDispatcher启用了异步线程,需注意线程资源分配

掌握了这套机制,下次看到@RocketMQMessageListener时,你的眼前会自动播放这套启动流程动画 😄


互动环节 有同学遇到过Consumer启动时线程池阻塞的情况吗?欢迎分享你的排查故事!

相关推荐
崎岖Qiu1 小时前
【JVM篇11】:分代回收与GC回收范围的分类详解
java·jvm·后端·面试
许苑向上3 小时前
Spring Boot 自动装配底层源码实现详解
java·spring boot·后端
超级小忍6 小时前
深入浅出:在 Spring Boot 中构建实时应用 - 全面掌握 WebSocket
spring boot·后端·websocket
没有bug.的程序员6 小时前
《Spring Security源码深度剖析:Filter链与权限控制模型》
java·后端·spring·security·filter·权限控制
无责任此方_修行中6 小时前
不止是 AI 热潮:AWS 2025 技术峰会带给我的思考
后端·架构·aws
lang201509287 小时前
Apache Ignite 与 Spring Boot 集成
spring boot·后端·apache·ignite
Asthenia04127 小时前
深入剖析 Spring Boot 请求处理链路与 Servlet 的本质
后端
旧时光巷7 小时前
【Flask 基础 ①】 | 路由、参数与模板渲染
后端·python·零基础·flask·web·模板渲染·路由系统
小醉你真好7 小时前
Spring Boot 数据源配置中为什么可以不用写 driver-class-name
spring boot·后端·源代码管理
SirLancelot17 小时前
数据结构-Set集合(一)Set集合介绍、优缺点
java·开发语言·数据结构·后端·算法·哈希算法·set