瞬时大流量场景 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)
-
CHECK:Listener不能同时实现RocketMQListener接口和RocketMQReplyListener接口,也不能同时不实现RocketMQListener接口和RocketMQReplyListener接口
-
genericApplicationContext.registerBean("DefaultRocketMQListenerContainer_" + counter, createRocketMQListenerContainer);。这里会实际创建RocketMQListenerContainer,方法实现的内容如下:
- 实例化DefaultRocketMQListenerContainer并记录注解及其他相关信息,大多数字段都用了resolvePlaceholders解析,支持
${}
- 根据 Listener 是否有返回值(RocketMQListener、RocketMQReplyListener),赋值 container 中的 RocketMQListener字段值
- DefaultRocketMQListenerContainer 在 Bean 初始化完成后(afterPropertiesSet),会初始化 MQPushConsumer
- 如果@RocketMQMessageListener注解配置了 AK 和 CK ,则创建一个
RPCHook
,用于创建DefaultMQPushConsumer ,否则,创建一个不指定RPCHook
的DefaultMQPushConsumer。- DefaultMQPushConsumer实例化的时候会根据DefaultMQPushConsumer中的参数创建 defaultMQPushConsumerImpl = new DefaultMQPushConsumerImpl(this, rpcHook);
- 如果 Consumer 注解上配置了消息追踪(
enableMsgTrace
),则初始化一个AsyncTraceDispatcher,并注册到 defaultMQPushConsumerImpl
- 初始化consumer的所有参数,包括 consumeThreadMax、consumeTimeout、consumeMode、selectorType 等等
- 调用Hock方法
RocketMQConsumerLifecycleListener#prepareStart
,可以覆盖前面初始化的参数值,比如 consumeThreadMax、pullBatchSize
- 如果@RocketMQMessageListener注解配置了 AK 和 CK ,则创建一个
- 实例化DefaultRocketMQListenerContainer并记录注解及其他相关信息,大多数字段都用了resolvePlaceholders解析,支持
-
rocketMQListenerContainer.start();
-
DefaultMQPushConsumer.start() Part1 => setContainerGroup(namespace, resourceWithOutNamespace)
-
DefaultMQPushConsumer.start() Part2 => defaultMQPushConsumerImpl.start():具体的实现逻辑很多,抽取我认为重要的:
-
默认情况下,广播模式的消息 offset 存储在本地文件(LocalFileOffsetStore),集群模式的 offset 存储在远程 broker(RemoteBrokerOffsetStore)
-
如果是顺序消费,consumeMessageService = new ConsumeMessageOrderlyService(),如果是并发消费,consumeMessageService = new ConsumeMessageConcurrentlyService(),实例化时会创建消费用的线程池,线程数使用指定的线程数,BlockingQueue使用 LinkedBlockingQueue。
-
实例化完成后会调用 start()方法,用于定时清理过期消息
-
线程池consumeExecutor用于pullAPIWrapper拉取消息成功后,将拉取到的消息交给消费服务进行业务处理
-
-
将 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()
:启动生产者实例,内容较多,暂时忽略
-
-
DefaultMQPushConsumer.start() Part3 => 如果enableMsgTrace,
traceDispatcher.start()
-
更新运行状态
DefaultMQPushConsumer.this.setRunning(true)
-
pullBatchSize todo
org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#pullMessage
整体流程时序图(不关注细节)
完整流程时序图
(不能同时/不实现两个接口) 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自动化装配
当Spring容器启动时,RocketMQAutoConfiguration
这个自动配置类 会导入关键配置ListenerContainerConfiguration
。它在容器就绪后执行一个重要任务:扫描所有带@RocketMQMessageListener
注解的Bean,并创建对应的消息监听容器。
本质:把简单的注解声明 ➡️ 转化为可运行的RocketMQ消费者
二、详解:三大核心实现阶段
▶️ 阶段1:容器注册(registerContainer)
java
// 伪代码示例:容器注册本质
for (Bean bean : allBeans) {
if (has @RocketMQMessageListener) {
checkListenerType(bean); // 合规检查
createContainer(bean); // 容器创建
}
}
🔍 关键动作:
-
合规检查(重要!)
- ❌ 禁止:同时实现
RocketMQListener
和RocketMQReplyListener
- ❌ 禁止:两个接口都不实现
- ✅ 要求:必须且仅实现其中一个
- ❌ 禁止:同时实现
-
动态创建容器
scss// 实际创建逻辑 genericAppCtx.registerBean( "DefaultRocketMQListenerContainer_"+counter, () -> createRocketMQListenerContainer(anno, bean) );
- 解析注解参数(支持
${}
占位符) - 绑定Listener实现类(根据有无返回值区分类型)
- 解析注解参数(支持
▶️ 阶段2:容器初始化(afterPropertiesSet)
🔧 核心步骤:
-
消费者实例化
- 根据AK/SK配置创建
RPCHook
- 构建
DefaultMQPushConsumerImpl
核心组件
- 根据AK/SK配置创建
-
关键参数初始化
consumeThreadMax
(最大消费线程数)consumeMode
(消费模式:并发/顺序)selectorType
(消息过滤机制)
-
生命周期钩子
scss// 参数覆盖示例 RocketMQConsumerLifecycleListener.prepareStart(consumer);
- 可通过Hook覆盖初始化参数(如动态调整pullBatchSize)
▶️ 阶段3:容器启动(container.start())
🚀 Part1: 基础配置
- 设置ConsumerGroup(添加namespace防冲突)
🚀 Part2: 核心启动流程
关键实现:
-
消费服务创建
- 顺序消费:
ConsumeMessageOrderlyService
- 并发消费:
ConsumeMessageConcurrentlyService
- ⚡️ 初始化消费线程池(核心配置参数:
consumeThreadMax
)
- 顺序消费:
-
MQClientInstance启动
- 周期性任务:
- 30秒/次:更新Topic路由信息
- 5秒/次:持久化消费位点
- 30秒/次:向Broker发送心跳
arduino// 线程池初始化示例 consumeExecutor = new ThreadPoolExecutor( this.defaultMQPushConsumer.getConsumeThreadMin(), this.defaultMQPushConsumer.getConsumeThreadMax(), 1000 * 60, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(queueSize) );
- 周期性任务:
-
消息拉取服务
-
PullMessageService核心逻辑:
scss// 简化的轮询逻辑 while (!stopped) { PullRequest request = pullRequestQueue.take(); pullMessage(request); // 触发实际拉取 }
-
流控策略:检查消息堆积/内存占用/队列锁状态
-
🚀 Part3:收尾工作
- 启动消息轨迹跟踪(如配置enableMsgTrace)
- 更新容器状态:
running=true
三、总结:RocketMQ+Spring协作全景图
💡 避坑指南:
- 广播模式offset存本地,重启需考虑位点同步问题
- 顺序消费线程数设置过大会导致文件锁竞争
- TraceDispatcher启用了异步线程,需注意线程资源分配
掌握了这套机制,下次看到
@RocketMQMessageListener
时,你的眼前会自动播放这套启动流程动画 😄
互动环节 有同学遇到过Consumer启动时线程池阻塞的情况吗?欢迎分享你的排查故事!