RocketMQ 4.x 遷移至 5.x:選型、切換與踩坑經驗
RocketMQ 从 4.x 迁到 5.x:选型、切换和踩过的坑
一次阿里雲 RocketMQ 4.x 遷移至 5.x 的實戰採用單寫雙讀方案,完成切換後業務平穩運行,沒有丟失訊息。遷移將客戶端由 4.9.7 升級至 5.3.1,並處理延遲訊息的兼容差異,包括按訊息類型分流 Topic。文章亦記錄容量測試、舊端訊息收尾及切換前的核對事項。
2026-10-29 10
版权
版权声明:
本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议》和 《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
简介: 本文记录阿里云RocketMQ 4.x→5.x迁移实战:采用单写双读方案,攻克延迟消息兼容、容量选型、双端衔接等难题,实现零消息丢失、业务无感切换,涵盖测试设计、踩坑复盘与经验沉淀。
最近和同事一起完成了阿里云 RocketMQ 4.x 到 5.x 的迁移,业务已经平稳运行两周。过程并非一路顺利:先迁的小项目暴露了延迟消息的兼容问题,我修复组件、业务同事补救受影响的消息后,才继续迁移其他项目。这次没有丢消息。
我主要做消息组件升级、容量与成本调研,以及配合业务组接入。之前待过的公司基本没用过 MQ,这是我第一次完整参与迁移。选多大的实例、新旧消息怎么衔接、延迟消息哪里不兼容,都是这次实际碰到的问题,下面按过程记一下。
本文已对业务相关信息做脱敏处理,主要记录方案取舍、验证过程和踩坑经历。
一、有哪些迁移办法?
迁移之前,需要先弄清楚哪些应用在发、哪些在收,用到了哪些消息类型。至于怎么切,要看业务能不能暂停、生产和消费能不能分开控制,以及现有的发布能力。常见做法有下面几种:
| 做法 | 适用条件与需要处理的问题 |
|---|---|
| 停写、排空后切换 | 业务允许暂停生产;还要处理未到期的延迟消息、重试及恢复写入后的验证 |
| 按业务、Topic 或实例分批切换 | 能识别一批消息的完整生产消费链路,并控制配置和发布范围;HTTP 请求灰度不等于 MQ 链路也已隔离 |
| 新旧并行,单路写入(新)、两路消费(新旧) | 客户端或组件能同时连接两端;切换生产去向后继续处理旧端消息,并核对幂等、顺序和收尾条件 |
| 双写或消息复制后再切换 | 需要解决部分写入成功、重复处理、复制延迟及位点衔接;不是多发一份就自动具备回滚能力 |
这些做法可以组合,没有一套适用于所有系统的唯一标准。例如,阿里云的 4.x 与 5.x 差异及升级说明
介绍了结合双读双写与分阶段发布的迁移方案,具体步骤仍需结合客户端兼容性和业务消息类型确定。
我们后来采用的是单写双读,切换主要靠消息组件、配置和项目发布配合,具体经过放在后面。复盘时再对照公开案例,思路其实相近。只是当时可用的发布与流量控制能力有限,生产者是否切完、旧端还有哪些消息,都需要逐项核对。
二、实例买多大?
购买规格时,日消息量可以帮助了解规模,但不能直接回答峰值需要多大容量。
同样的消息总量,均匀发送和集中突发,对实例的要求不一样。发送之后还有消费;多个消费组各自订阅同一批消息,也会产生不同的投递负载。发生失败重试,或者要加速处理积压时,还需要额外余量。
选型时,我主要看这些:
- 生产和消费的流量,以及峰值持续时间。
- 实例基础规格与弹性能力。
- 客户端是否限速,业务处理速度是否成为瓶颈。
- 消费组数量,以及堆积恢复时的追赶流量。
- 不同配置下的流控、重试和最终发送失败情况。
这里容易混淆的是“消费组”和“消费实例”。同一个消费组增加实例,通常是在分摊该组的消费任务;不同消费组分别订阅,则需要各自处理一份消息。只测发送吞吐,很容易漏掉后面这部分容量。
较小规格配弹性,和直接提高基础规格,费用和突发时的表现也不一样。我把这两种配置都放进测试里,再看发送侧需不需要限速。
三、必须要跑的测试
为了反复比较这些配置,我准备了可调参数的测试程序。生产侧能调消息总数、并发、目标发送速率和重试参数;消费侧能调线程数、处理延迟和随机失败率。
这部分也用了 AI:帮我规划测试、分析各轮结果,一起看不同配置的差异,讨论下一轮还要测什么。建议是否适用,再结合实测结果和业务需求判断。
测试从小批量开始,逐步增加负载,每轮留下配置、日志和监控,主要跑了这些场景:
| 场景 | 要回答的问题 |
|---|---|
| 小批量收发 | 连接、权限、资源和基本收发链路是否正常 |
| 逐步提高发送速率 | 瓶颈来自测试程序、客户端限制,还是实例流控 |
| 消息堆积 | 消费落后时系统如何表现,扩容和加线程能否改善 |
| 随机失败 | 失败后是否重投,积压是否能恢复 |
| 多消费组 | 多组投递带来的负载是否被容量评估覆盖 |
| 规格、弹性与限速对比 | 不同配置下的吞吐、流控和重试如何变化 |
客户端没放开,不能说实例不够用
早期测试里,消费速率停在一个相对固定的位置。检查后发现,组件本身设置了消费限速。这个限制不调整,即使换更大的实例,也未必能看到明显改善。
我把消费限速改成可配置项,再提高测试负载。后面换规格之前,也会先检查测试程序和客户端有没有把速率限制住。
配置的目标速率也不等于实际速率。线程调度、发送耗时和节流实现都会影响结果,因此我对照实际完成量与耗时看吞吐,而不只看配置值。
规格和限速要一起看
规格对比中,较小规格在突发测试下出现了流控,开启弹性后,测试程序仍有部分发送在有限重试后未成功完成。较大规格的对应轮次则在重试后完成发送;进一步限制发送速率,重试次数也有所减少。
这里的发送失败,是重试耗尽后仍未确认发送成功,不是 Broker 收到消息后又把它丢了。业务接入时,这类失败还得记录下来,安排补偿或人工处理。
堆积恢复不能只看总耗时
我还通过增加模拟处理延迟,主动制造堆积,再观察增加消费实例、提高线程数后的变化,最后取消模拟延迟完成排空。
这轮中间有等待、扩容、滚动变更,也调了多次参数,所以我分阶段看结果。如果只拿从消息注入到全部排空的总时间,很难说清某一次扩容到底起了多大作用。
测试也让我重新检查了统计方式:进程启动后的累计平均吞吐可能包含很长的空闲期;消费回调次数可能包含失败与重试。它们都不能直接代替活跃窗口的处理能力或唯一成功消息数。
四、同事的一句话,改变了切换思路
最初我们想分两步切换:先发布生产侧,让新消息发往新实例;旧消费者继续处理旧实例里的消息,等旧消息消费完,再发布切换到新实例的消费者。
但有些模块既生产消息,也消费消息,没法简单地把它们分成“生产者先发、消费者后发”两批。如果没有额外的配置或连接控制,一次发布就可能同时改变生产和消费的去向,原先设想的两步切换就不好安排。
真准备按这个方案发版时,我心里还是没底,担心切换时遗漏旧消息。和业务端同事讨论时,一位新加入的同事提醒我:能不能让新旧 MQ 同时接入,新消息只向新端发送,消费者两边都保留,继续处理旧端存量?
听完他的建议,我才意识到,之前一直纠结怎么分两批发布,其实可以让新旧两端的消费者同时运行。不必等旧消息消费完,才开始消费新端的消息。
我随后去查客户端 SDK 兼容性,确认这次使用的新客户端 SDK 可以连接旧实例,再修改组件、发版。这里沿用的是 DefaultMQProducer / DefaultMQPushConsumer 接入方式,没有换成不能连接此次旧端 4.x 实例的 5.x gRPC SDK。
过渡方案可以概括为:
停止向旧实例生产后:
新消息 ──→ 新 MQ ──→ 新端消费者 ──┐
├──→ 业务处理
旧端存量 ─→ 旧 MQ ──→ 旧端消费者 ─┘
组件里的主生产者向新实例发送,旧连接只负责消费,不承担发送和资源初始化。同时,我增加了旧消费连接开关,方便后续结束过渡。
这是两路消费,不会把同一条消息写入两个实例,也不会把旧消息复制到新端。滚动发布期间,还要逐一核对生产者的实际去向,确认哪些已经切换、哪些仍在向旧端发送。
什么时候可以关掉旧连接?
这个开关留作过渡结束后的收尾手段。旧端堆积归零后,未到期的延迟消息和后续重试仍可能需要消费。因此,我把下面几项列为关闭旧连接前需要核对的内容:
- 是否还有生产者向旧端发送。
- 存量消息是否完成业务处理。
- 是否还有未到期的延迟消息、待重试消息或需要处理的死信。
- 消费位点是否符合预期,业务结果是否完成核对。
旧连接具体何时关闭,要等这些核对完成后再决定。两端消息之间的全局顺序、重试和补救时的幂等,也需要业务单独处理,双路消费本身不保证恰好一次执行。
五、体量小的项目先迁移时发现了遗漏的延迟消息差异
实际推进时,我们先选了一个小项目迁移。这个项目暴露了前期调研漏掉的兼容问题:在本次使用的阿里云实例及配置下,源端 4.x 的同一个 Topic 可以承载普通消息和延迟消息,目标端 5.x 则需要按消息类型区分 Topic。
前期我测了收发和吞吐,却漏查了这个消息类型差异。
发现问题后,我立即在组件中补上延迟消息独立 Topic 的分流,以及 DELAY 类型 Topic 的创建,随后发版。业务同事及时编写补救程序,完成受影响消息的处理,之后再推进后续项目迁移。
好在这次先迁的是小项目,修复后才继续扩大范围。测试清单里漏掉的延迟消息,最终是在实际接入时发现的。
六、收发之外,还改了启动顺序
客户端从 4.9.7 升到 5.3.1 后,除了包路径和依赖适配,组件层还处理了资源初始化、消费限速、消费起始位点和日志控制等问题。
启动顺序也做了调整:生产者改为同步启动,失败时向外抛出,避免应用已经启动,却还不能发送消息;共享客户端身份的消费者则串行创建,避免首次初始化时出现竞态。
七、下次我会提前确认的几件事
这次做完,有几件事我会放到下一次迁移的准备阶段:
- 把消息类型列全。 普通消息、延迟消息分别验证,不等先行项目碰巧用到才发现问题。
- 按实际模块安排发布。 先看哪些模块既生产又消费,再决定生产和消费怎么切。
- 换规格前检查限速。 把测试程序、组件和客户端的限制查清楚,再看实例容量。
- 提前约定旧连接的关闭条件。 谁来确认旧端停止写入、核对剩余消息和业务结果,在切换前说清楚。
这次从方案讨论到实际切换,得到了同事不少帮助。双路消费的思路是同事提醒的,我再继续调研和实现;业务同事配合接入、处理受影响的消息,负责人也帮我了解业务背景、讨论方案。谢谢大家。
参考资料
- 阿里云 RocketMQ 4.x 与 5.x 差异及升级说明:版本兼容性、消息类型约束及迁移方案的官方说明。
- 大规模 ActiveMQ 到 RocketMQ 的迁移实践(英文):提供单写双读、消费者先行的实践参考,迁移对象与本文不同。这是复盘时对照的资料,并非当时方案的来源。
來源:阿里雲開發者社區 AI · developer.aliyun.com