谁懂啊!测试环境 RocketMQ 延迟消息崩了,罪魁祸首是个…

家人们谁懂啊!昨天在测试环境对接 RocketMQ 5.0 延迟消息,给我整了个大活 ------ 本地 5.0/5.3.2 版本跑得飞起,测试环境一发送就报CODE:14,屏幕上 "service not available" 几个字看得我脑壳发麻。和运维折腾了一下午,最后发现凶手居然运维在配置文件里的一个空格!这波踩坑经历,简直是 "低级错误教做人" 现场。

一、现场:测试环境的 "迷之报错",本地却啥事没有

先上灵魂暴击的报错日志(就是我当时甩在群里的原版,一字没改):

less 复制代码
2025-12-03 17:19:56.172 ERROR 27112 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.apache.rocketmq.client.exception.MQClientException: Send [3] times, still failed, cost [531]ms, Topic: test-delay-msg, BrokersSent: [broker-a, broker-a, broker-a]
See http://rocketmq.apache.org/docs/faq/ for further details.] with root cause

org.apache.rocketmq.client.exception.MQBrokerException: CODE: 14  DESC: service not available now. It may be caused by one of the following reasons: the broker's disk is full [CL:  0.53 CQ:  0.53 INDEX:  0.53], messages are put to the slave, message store has been shut down, etc. BROKER: 172.28.200.4:10911
For more information, please visit the url, http://rocketmq.apache.org/docs/faq/
        at org.apache.rocketmq.client.impl.MQClientAPIImpl.processSendResponse(MQClientAPIImpl.java:779) ~[rocketmq-client-5.0.0.jar:5.0.0]
        at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessageSync(MQClientAPIImpl.java:619) ~[rocketmq-client-5.0.0.jar:5.0.0]
        at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:601) ~[rocketmq-client-5.0.0.jar:5.0.0]
        at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:545) ~[rocketmq-client-5.0.0.jar:5.0.0]
        at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendKernelImpl(DefaultMQProducerImpl.java:907) ~[rocketmq-client-5.0.0.jar:5.0.0]
        at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:643) ~[rocketmq-client-5.0.0.jar:5.0.0]
        at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1426) ~[rocketmq-client-5.0.0.jar:5.0.0]
        at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1369) ~[rocketmq-client-5.0.0.jar:5.0.0]

群里运维求助的时候我都懵了:

  • 本地搭的 RocketMQ 5.0、5.3.2,发test-delay-msg这个 Topic 的延迟消息,秒发送、秒投递,10 秒延迟精准得很;
  • 切到测试环境,代码一行没改、配置项也都配了,重启 Broker 三次,还是连番报 CODE:14,日志里 "service not available" 快把我眼睛晃瞎了;
  • 甚至怀疑过是不是测试环境的 RocketMQ 不是官方版本,查了安装包,妥妥的官网下载的 5.0 版本... 当时我盯着那行disk is full [CL: 0.53 CQ: 0.53 INDEX: 0.53]还傻呵呵去查磁盘 ------ 使用率才 53%,离满格差十万八千里,这报错纯纯 "误导人"!

二、排查:从 "怀疑人生" 到揪出 "空格刺客"

第一波排查:把能试的都试了,全白搭

我跟这个 BUG 死磕的过程,主打一个 "病急乱投医":

  1. 查网络:telnet 172.28.200.4:10911,通的;ping Namesrv,延迟正常;
  2. 查权限:Topic test-delay-msg的读写权限全开,Producer Group 也没配错;
  3. 查延迟等级:用的默认 3 级(10 秒),没超 1-18 的范围;
  4. 重启大法:Broker、Namesrv、应用服务轮着重启,报错纹丝不动;
  5. 对比版本:本地和测试环境都是 rocketmq-client-5.0.0.jar,版本完全一致。

当时我都快怀疑是不是测试环境的 Broker 被 "下咒" 了,甚至翻了 RocketMQ 官方 FAQ,结果 FAQ 里说的 "磁盘满、从节点存储、消息存储关闭",我这边一个都没中!

第二波排查:逐字符对比配置文件,发现 "隐形凶手"

绝望中我把本地和测试环境的broker.conf拉到一起逐行对比 ------ 就差拿放大镜看了,终于!在测试环境的配置行里,发现了那个藏在数字后面的空格(就是我之前提的 "后面有空格" 的坑):

就这一个不起眼的空格,直接把 RocketMQ 5.0 的 Broker 干废了!

三、破案:为啥一个空格能搞崩延迟消息?

RocketMQ 5.0 的延迟消息全靠 "定时轮(timerWheel)" 这个核心功能撑着 ------ 相当于 Broker 里有个精准的 "闹钟",到点就把延迟消息推给消费者。而这个 "闹钟" 能不能启动,全看timerPrecisionMs这些配置项的解析结果:

  1. RocketMQ 5.0 对配置解析的容错性比老版本低了 N 个档次,等号后多一个空格,它就把1000 (带空格)当成 "非法数字";
  2. 非法配置直接导致定时轮服务初始化失败,延迟消息模块直接躺平,返回 "service not available"(CODE:14);
  3. 更坑的是,这种配置错误不会在 Broker 启动日志里报错,只会在发延迟消息时炸锅,纯纯的 "暗箭伤人"!

四、救场:三步送走这个 "空格刺客"

第一步:给配置文件 "刮胡子"------ 删光多余空格

把测试环境broker.conf里所有配置项的多余空格全清掉,保证key=value是 "无缝衔接" 的状态:

ini 复制代码
# 修正后的正确配置
timerWheelEnable = true
timerPrecisionMs=1000
timerRollWindowSlot=86400

别嫌麻烦,每一行都要检查!尤其是复制粘贴来的配置,很容易带空格。

第二步:重启 Broker,验证配置是否生效

bash 复制代码
# 先停掉摆烂的Broker
sh mqshutdown broker
# 用干净的配置重启
nohup sh mqbroker -c ../conf/broker.conf &
# 检查定时轮服务是否正常启动
sh mqadmin getBrokerConfig -n 127.0.0.1:9876 -b 172.28.200.4:10911 | grep "timerWheelEnable"

只要输出timerWheelEnable=true,说明 "闹钟" 终于正常工作了。

第三步:测试延迟消息,终于扬眉吐气

重新写了行测试代码,手都抖着点了运行:

ini 复制代码
// 测试延迟消息发送
DefaultMQProducer producer = new DefaultMQProducer("test-producer-group");
producer.setNamesrvAddr("172.28.200.4:9876");
producer.start();

Message msg = new Message("test-delay-msg", "终于能发延迟消息了!".getBytes());
msg.setDelayTimeLevel(3); // 10秒延迟
SendResult result = producer.send(msg);
System.out.println("发送成功!消息ID:" + result.getMsgId());

producer.shutdown();

10 秒后,消费者日志弹出 "终于能发延迟消息了!"------ 那一刻,我直接在工位上长舒一口气!

五、血的教训:RocketMQ 5.0 延迟消息的 "细节暗杀"

  1. 配置文件别瞎加空格:5.0 版本对配置格式是 "处女座级别的严格",等号前后、行尾的空格都可能触发隐藏 BUG;
  2. 延迟消息 = 定时轮 + 配置 :发延迟消息前,先查timerWheelEnable是不是 true,别等报错了才回头;
  3. 本地能跑≠测试环境能跑:环境差异要 "逐字符对比",尤其是配置文件,别嫌麻烦,早对比早省心;
  4. CODE:14 别只看磁盘:这个报错的误导性极强,磁盘满只是其中一个原因,配置错误才是高频坑!
  5. 最后友情附赠个小技巧:把配置文件复制到记事本里开 "显示所有字符",空格、换行这些 "隐形刺客" 立马现形!
相关推荐
milixiang1 小时前
项目部署时接口短暂访问异常问题修复:Nacos+Gateway活跃节点监听
后端·spring cloud
Stream1 小时前
加密与签名技术之密钥派生与密码学随机数
后端·算法
绝无仅有1 小时前
redis缓存功能结合实际项目面试之问题与解析
后端·面试·架构
Stream1 小时前
加密与签名技术之哈希算法
后端·算法
z***D6481 小时前
SpringBoot 新特性
java·spring boot·后端
IT_陈寒2 小时前
JavaScript 性能优化:7个 V8 引擎隐藏技巧让你的代码提速200%
前端·人工智能·后端
qq_12498707532 小时前
基于springboot的糖业信息咨讯网站(源码+论文+部署+安装)
java·spring boot·后端·毕业设计·毕设
fanruitian2 小时前
Springboot 示例模版
java·spring boot·后端
Dolphin_Home3 小时前
深度解析:SpringBoot 静态类调用 Bean 的底层逻辑与最优实践
java·spring boot·后端