Files
cn-rdms/rdms-framework/rdms-spring-boot-starter-mq/MQ_REFACTOR_PLAN.md
2026-03-11 19:32:37 +08:00

233 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MQ 改造方案(评审稿)
## 1. 背景与目标
你当前的业务策略是:
1. 单体部署优先使用 Redis 作为 MQ。
2. 微服务部署优先使用 RocketMQ 作为 MQ。
3. RabbitMQ、Kafka 暂时不作为主路径,仅保留包结构隔离,不参与核心链路。
本方案目标是:
1. 在不破坏现有系统的前提下,建立可切换的 MQ 入口。
2. 把“切换成本”收敛到配置层,而不是业务代码层。
3. 采用分阶段改造,先低风险,后统一抽象。
## 1.1 本期非目标(避免范围蔓延)
本期改造明确不包含:
1. 严格顺序语义保障(全链路有序消费)。
2. 统一死信队列DLQ治理体系。
3. 事务消息与最终一致性框架化封装。
4. 多机房容灾级别的 MQ 治理。
说明:
1. 本期先完成“可切换、可回滚、可观测”的基础能力。
2. 复杂语义能力后续在 RocketMQ 路线下单独立项。
## 2. 当前实现现状(简版)
当前 `rdms-spring-boot-starter-mq` 的真实情况:
1. Redis 能力最完整包含模板、监听器抽象、Stream 补偿与清理任务。
2. RabbitMQ 只有 `MessageConverter` 级别自动配置,未形成统一收发抽象。
3. RocketMQ、Kafka 在该 starter 中没有对应自动配置主链路,实际接入主要在 WebSocket 模块。
当前 `rdms-system` 默认配置:
1. `rdms.websocket.sender-type: local`,即默认并未走 MQ 分发链路。
## 3. 改造原则
1. 先配置统一,再接口统一,最后再清理非主路径。
2. 任何阶段都必须可回滚,且回滚只改配置不改代码。
3. 保持 Rabbit/Kafka 包路径存在,避免一次性大删导致历史分支合并困难。
4. 先让 WebSocket 场景打通可切换,再考虑扩展到业务 MQ。
## 4. 目标架构(落地后)
统一引入配置:
1. `rdms.mq.type=redis|rocketmq`
统一行为:
1. 单体环境配 `redis`
2. 微服务环境配 `rocketmq`
3. WebSocket `sender-type` 跟随 `rdms.mq.type`,避免双配置。
建议方式:
1. `rdms.websocket.sender-type: ${rdms.mq.type}`
## 4.1 配置矩阵(最小可运行)
### 单体Redis
```yaml
rdms:
mq:
type: redis
websocket:
sender-type: ${rdms.mq.type}
spring:
data:
redis:
host: 127.0.0.1
port: 6379
```
### 微服务RocketMQ
```yaml
rdms:
mq:
type: rocketmq
websocket:
sender-type: ${rdms.mq.type}
rocketmq:
name-server: 127.0.0.1:9876
producer:
group: rdms-producer-group
```
备注:
1. 微服务切 Rocket 前,必须先补齐 topic、consumer-group 等业务配置。
2. 建议在 dev/local 保留 Redis 配置,便于快速回滚。
## 4.2 命名规范Topic / Channel / StreamKey
建议统一命名规则,避免后续混乱:
1. Redis Channel / StreamKey`{env}:{domain}:{event}`
2. Rocket Topic`{env}_{domain}_{event}`
3. ConsumerGroup`{app}-{domain}-cg`
示例:
1. `dev:system:notify`
2. `prod_system_notify`
3. `rdms-system-notify-cg`
## 5. 分阶段实施计划
## 阶段 A低风险建议先做
目标:只做配置层统一,不动大规模代码。
实施项:
1. 新增配置项 `rdms.mq.type`,默认值 `redis`
2. 修改环境配置,使 `rdms.websocket.sender-type` 引用 `rdms.mq.type`
3. 本阶段不删除 Rabbit/Kafka 代码,不变更包结构。
收益:
1. 切换 Redis/Rocket 只改配置。
2. 不触碰核心业务逻辑,风险最低。
回滚:
1.`rdms.websocket.sender-type` 改回固定值即可。
## 阶段 B中风险接口统一
目标:抽象统一 MQ 发送接口,减少业务对具体中间件的耦合。
实施项:
1. 新增统一接口,例如 `UnifiedMqSender`
2. 提供 `RedisMqSender``RocketMqSender` 两个实现。
3.`@ConditionalOnProperty` 根据 `rdms.mq.type` 注入唯一实现。
4. 在 WebSocket 发送链路优先替换为统一接口调用。
5. 统一消息契约,至少包含标准消息头:
`msgId``bizKey``timestamp``producer``traceId``version`
收益:
1. 业务层不再直接依赖 `RedisMQTemplate``RocketMQTemplate`
2. 后续切换中间件时改动面可控。
3. 消息协议可演进,减少多团队并行开发冲突。
回滚:
1. 切回原 sender Bean 注入方案,保留统一接口代码但不启用。
## 阶段 C可选结构收敛
目标:让非主路径代码“隔离可见但不激活”。
实施项:
1. Rabbit/Kafka 相关自动配置入口默认关闭。
2. 保留目录与类,增加注释标识 `reserved``deprecated`
3. 文档明确“当前生产主路径仅 Redis/Rocket”。
收益:
1. 新成员不会误判项目“全量支持四种 MQ”。
2. 后续若要恢复 Rabbit/Kafka可低成本重新开启。
回滚:
1. 打开对应配置开关即可恢复。
## 6. 影响面评估
主要影响模块:
1. `rdms-framework/rdms-spring-boot-starter-mq`
2. `rdms-framework/rdms-spring-boot-starter-websocket`
3. `rdms-system/rdms-system-boot` 配置文件
主要风险点:
1. Redis 与 Rocket 的消费语义不完全一致,幂等要在业务侧兜底。
2. 广播模型下,多实例重复消费属于预期,业务处理要避免副作用。
3. 配置切换后,缺失 Rocket 连接配置会导致启动失败。
4. 现有代码中 Kafka 消费类注解存在明显可疑项,改造时应单独复核。
5. 缺少统一幂等策略时Rocket 与 Redis 切换后可能放大重复消费副作用。
6. 配置缺失时若做了自动降级,可能导致“误以为切到 Rocket实际走本地/Redis”。
## 6.1 幂等策略(建议作为阶段 B 配套)
最小策略建议:
1. 每条消息必须带 `msgId``bizKey`
2.`bizKey` 作为业务幂等键(例如订单号、任务号)。
3. 消费前先做幂等检查,消费成功后写入幂等记录。
4. 幂等记录建议放 Redis设置合理 TTL如 3~7 天,按业务回放窗口)。
说明:
1. `msgId` 解决“传输级去重定位”,`bizKey` 解决“业务级幂等”。
2. 幂等是切换 MQ 前置条件,不应后补。
## 6.2 故障切换策略(明确 fail-fast
建议默认策略:
1. 目标 MQ 不可用时 `fail-fast`,启动或发送直接失败并告警。
2. 禁止隐式降级到 local避免行为不透明。
可选策略:
1. 在开发环境允许显式降级(需开关控制,并打印告警日志)。
## 7. 验收清单(每阶段都要过)
1. 单体环境 `rdms.mq.type=redis` 可启动、可发送、可消费。
2. 微服务环境 `rdms.mq.type=rocketmq` 可启动、可发送、可消费。
3. WebSocket 在两种模式下都能完成跨实例消息投递。
4. 关键链路日志可定位发送端与消费端。
5. 切换只改配置,不改代码。
6. 回滚路径已验证。
7. 幂等校验通过(重复消息不会造成业务副作用)。
8. 关键指标可观测并有告警(发送失败、消费失败、积压、重试)。
## 7.1 观测与告警建议
建议统一接入以下指标:
1. `mq_send_total` / `mq_send_fail_total`
2. `mq_consume_total` / `mq_consume_fail_total`
3. `mq_backlog_size`
4. `mq_retry_total`
建议阈值(示例):
1. 5 分钟发送失败率 > 1% 告警。
2. 连续 10 分钟消费失败率 > 0.5% 告警。
3. 积压超过基线 3 倍且持续 10 分钟告警。
## 8. 推荐实施顺序与工时
推荐顺序:
1. 先做阶段 A。
2. 阶段 A 稳定后再做阶段 B。
3. 阶段 C 按团队节奏处理。
粗略工时(含联调):
1. 阶段 A0.5 天。
2. 阶段 B1 天到 1.5 天。
3. 阶段 C0.5 天。
## 8.1 上线与回滚步骤(建议)
上线顺序:
1. 开发环境完成阶段 A验证双配置切换。
2. 预发环境灰度 1 个实例切换目标 MQ。
3. 小流量观察 1 天,确认无异常后全量。
回滚触发条件(任一满足即回滚):
1. 消费失败率持续超过阈值。
2. 积压持续增长且无法在观察窗口内回落。
3. 关键业务出现重复消费副作用。
回滚动作:
1. 只回滚 `rdms.mq.type``rdms.websocket.sender-type` 配置。
2. 不回滚代码,确保恢复路径最短。
## 9. 当前建议
你现在对项目结构还在熟悉期,建议先做阶段 A 的评审与验证,不直接进入阶段 B。
这样能先拿到“可切换能力”,同时把改造风险控制在最低范围。