一、💡 一句话理解
核心结论
Consumer 是一个正在干活的程序实例;Consumer Group 是一组执行相同逻辑的 Consumer。不同 Group 各自消费一遍,同一 Group 内的实例通常共同分担。
Group ID 相同:共同分担
Group ID 不同:各自消费前置概念:2. MQ 核心角色与代码对应。
二、🧭 Consumer 和 Consumer Group 的区别
假设 HRMS 部署三台机器:
HRMS Consumer Group
├── HRMS 实例 1:Consumer 1
├── HRMS 实例 2:Consumer 2
└── HRMS 实例 3:Consumer 3- Consumer 是一个实际运行的实例。
- Consumer Group 是三个实例共同使用的消费身份。
- 同组实例应使用相同的 Topic、Tag 和消费逻辑。
配置中的 Group ID:
spring.sofamq.normal.consumers[7].group-id=GID_CIC_MIDABOSS_ACL_ACCOUNT_MODIFY
spring.sofamq.normal.consumers[7].topic=TP_CIC_MIDABOSS_SSO_ACCOUNT_MODIFY
spring.sofamq.normal.consumers[7].tag=TG_CIC_MIDABOSS_SSO_ACCOUNT_MODIFY可以翻译成:
我是 ACL Group 中的一个 Consumer,
订阅账户变更 Topic,
只接收符合指定 Tag 的消息。三、🔀 不同 Group 为什么都能收到消息
TP_CIC_MIDABOSS_SSO_ACCOUNT_MODIFY
│
├── ACL Group
│ ├── ACL 实例 1
│ └── ACL 实例 2
│
└── HRMS Group
├── HRMS 实例 1
└── HRMS 实例 2一条账户变更消息会由 ACL Group 和 HRMS Group 分别处理一次。两个 Group 还会分别保存自己的消费进度:
ACL Group 已处理到第 100 条
HRMS Group 已处理到第 97 条ACL 处理成功不会代替 HRMS 确认,两个消费身份互不影响。
四、👥 集群消费 CLUSTERING
// 设置当前 Consumer 属于哪个 Group。
properties.setProperty(PropertyKeyConst.GROUP_ID, groupId);
// CLUSTERING:同组多个实例共同分担消息。
properties.setProperty(
PropertyKeyConst.MESSAGE_MODEL,
PropertyValueConst.CLUSTERING
);假设同组有三个实例:
消息 1 → 实例 A
消息 2 → 实例 B
消息 3 → 实例 C
消息 4 → 实例 A一条消息通常只由组内某一个实例处理,适合订单处理、数据同步、账户变更和异步任务。
增加实例通常可以提高消费能力,但实际并行度还受 Queue 数量、线程数和单条处理速度影响。
五、📢 广播消费 BROADCASTING
// BROADCASTING:同组每个实例都处理每条消息。
properties.setProperty(
PropertyKeyConst.MESSAGE_MODEL,
PropertyValueConst.BROADCASTING
);假设组织服务部署三台机器:
同一条缓存失效消息
├── 实例 A:清理自己的本地缓存
├── 实例 B:清理自己的本地缓存
└── 实例 C:清理自己的本地缓存项目中的典型代码:
consumer.subscribe(topic, tag, (message, context) -> {
OrganizationMessageModel org = parse(message);
// 本地缓存属于当前 JVM,每台机器都必须自己清理一次。
organizationLocalCache.invalidLocalOrgCacheByCode(
org.getBranchOrgCode()
);
return Action.CommitMessage;
});广播适合每台机器都有独立本地状态的场景,例如刷新本地缓存或本机配置。
六、🆚 三个容易混淆的层次
| 情况 | 一条消息怎样处理 |
|---|---|
| 不同 Consumer Group 订阅同一 Topic | 每个 Group 各自处理一遍 |
| 同一 Group 使用 CLUSTERING | 组内某一个实例处理 |
| 同一 Group 使用 BROADCASTING | 组内每个实例都处理 |
发布订阅不等于广播消费
不同 Group 各自收到消息属于发布订阅;同一个 Group 内每个实例都收到消息才是广播消费。
七、⚠️ Group ID 复用踩坑
曾经有两个不同应用误用了同一个 HRMS Group ID:
spring.sofamq.account.outsourced.group=GID_CIC_MIDABOSS_HRMS_ACCOUNT_OUTSOURCED_CHANG_STAFF两个应用都使用集群模式,Broker 会把它们当成同一应用的两个实例:
应用 A ─┐
├─ 相同 Group ID → Broker 认为属于同一组
应用 B ─┘ → 消息在两者之间分配于是可能出现:
消息 1 → 应用 A
消息 2 → 应用 B
消息 3 → 应用 A
消息 4 → 应用 B从单个应用看,就像随机丢消息。实际上消息被同组的另一个应用消费并提交了。
7.1 正确规则
只有同时满足下面条件的实例才应该共用 Group ID:
同一个业务应用
+ 相同 Topic
+ 相同 Tag
+ 相同消费逻辑
= 可以共用 Group ID 做水平扩容不同业务应用都需要收到消息时,应使用不同 Group:
账户变更 Topic
├── ACL Group → ACL 独立消费一遍
├── HRMS Group → HRMS 独立消费一遍
└── Other Group → 其他应用独立消费一遍7.2 修复后还要检查什么
修改 Group ID 后,新 Group 会拥有独立消费进度,不一定自动收到旧 Group 已确认的历史消息。还要检查:
- 旧 Group 的消息轨迹和实际消费客户端。
- 新旧应用的 Topic、Tag 和消费模式。
- 新 Group 的起始消费位点。
- 是否需要重置位点或重新投递历史消息。
- 补发前是否已经做好消费幂等。
八、🎤 面试时怎么说
Consumer 是实际运行的消费实例,Consumer Group 是一组执行相同逻辑的 Consumer,也是 MQ 管理订阅关系和消费进度的身份。不同 Group 订阅同一 Topic 时会各自消费一遍;集群模式下,同组实例共同分担消息;广播模式下,同组每个实例都处理每条消息。不同业务应用不能误用同一个 Group ID,否则 Broker 会把它们当成同组实例进行负载均衡,表现为每个应用只能收到部分消息。
九、📌 快速回顾
- Consumer 是实例,Consumer Group 是消费身份。
- 不同 Group 各自消费,同组实例通常共同分担。
- 集群模式适合业务处理,广播模式适合清理每台机器的本地状态。
- 不同应用复用 Group ID 会产生竞争消费和订阅不一致风险。
- 修改 Group ID 后还要检查历史消费位点。
下一篇:4. SOFAMQ 账户变更代码实践。