Files
cs-note/hhs/MQ/08-消息设计模式/28-MQ-Competing-Consumers-模式.md
T
2026-05-24 20:51:06 +08:00

154 lines
6.1 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.
---
tags: [MQ, 消息队列, 设计模式, 负载均衡]
create time: 2026-05-24 19:52
---
# MQ Competing Consumers 模式
## 概述
Competing Consumers(竞争消费者)模式通过多个消费者同时消费同一个队列来实现负载均衡,是提升消息处理吞吐量最直接的手段。本文深入讲解该模式的工作原理、分区分配算法、Consumer Rebalance 机制及其常见问题与优化方案。
## 正文
### 模式定义
传统单消费者模型中,一个队列只有一个消费者按顺序处理消息。当消息量增大时,单个消费者成为瓶颈。Competing Consumers 模式的核心思想很简单:**让多个消费者"抢"同一个队列的消息**,谁抢到谁处理,从而实现水平扩展。
```mermaid
graph LR
P["Producer"] --> Q["Queue"]
Q --> C1["Consumer 1"]
Q --> C2["Consumer 2"]
Q --> C3["Consumer 3"]
style P fill:#4A90D9,color:#fff
style Q fill:#F5A623,color:#fff
style C1 fill:#6EC1E0,color:#fff
style C2 fill:#6EC1E0,color:#fff
style C3 fill:#6EC1E0,color:#fff
```
### 与传统单消费者的对比
| 维度 | 单消费者 | Competing Consumers |
|------|---------|---------------------|
| 吞吐量 | 受单机性能限制 | 线性扩展(理论上) |
| 消息顺序 | 天然有序 | 需要额外保障(分区有序) |
| 可用性 | 消费者故障则停摆 | 单个消费者故障不影响整体 |
| 复杂度 | 低 | 高(需要处理 Rebalance) |
> [!question]
> Competing Consumers 提升了吞吐量,但牺牲了全局顺序性。如果你的业务需要"同一用户的操作有序",该如何在分区级别保障顺序?
### 分区分配算法详解
在 Kafka 中,Consumer Group 内的消费者通过分区分配算法决定"谁消费哪个 Partition"。不同的分配策略直接影响负载均衡效果和 Rebalance 开销。
#### Range(范围分配)
将 Topic 的 Partition 按序号范围分配给消费者。例如 6 个 Partition、3 个消费者:Consumer 0 拿到 [0,1],Consumer 1 拿到 [2,3],Consumer 2 拿到 [4,5]。
优点是简单直观,但如果多个 Topic 使用 Range 策略,可能导致某个消费者被分配到多个 Topic 的"头部"分区,造成负载不均。
#### Round-Robin(轮询分配)
将所有 Partition 轮询分配给消费者。6 个 Partition、3 个消费者:Consumer 0 拿到 [0,3],Consumer 1 拿到 [1,4],Consumer 2 拿到 [2,5]。
分配更均匀,但要求同一 Consumer Group 内所有消费者订阅的 Topic 完全一致,否则会出现分配异常。
#### Sticky(粘性分配)
在 Round-Robin 的基础上增加"粘性":Rebalance 时尽量保留原有的分配关系,只迁移必要的 Partition。这大幅减少了 Rebalance 期间的 Partition 迁移量。
#### Cooperative Sticky(协作式 Rebalance)
传统 Rebalance 是"Stop-the-World"——所有消费者暂停消费,等待分配完成。Cooperative Sticky 采用增量式 Rebalance:只暂停被迁移的 Partition,其余 Partition 继续消费。
```mermaid
graph TD
subgraph Before["Rebalance 前"]
B1["Consumer 1"] --> BP1["Partition 0, 1, 2"]
B2["Consumer 2"] --> BP2["Partition 3, 4, 5"]
end
subgraph After["Rebalance 后 (Cooperative Sticky)"]
A1["Consumer 1"] --> AP1["Partition 0, 1"]
A2["Consumer 2"] --> AP2["Partition 3, 4, 5"]
A3["Consumer 3 (新加入)"] --> AP3["Partition 2"]
end
Before --> After
style Before fill:#F5A623,color:#fff
style After fill:#6EC1E0,color:#fff
```
注意图中:Cooperative Sticky 只迁移了 Partition 2,其余 Partition 在 Rebalance 期间保持消费不中断。
### Consumer Rebalance 过程与问题
Rebalance 是 Competing Consumers 模式中最关键也最容易出问题的环节。
**触发条件**:消费者加入/离开 Group、消费者心跳超时、Topic Partition 数量变化。
**Stop-the-World 问题**:传统 Rebalance 期间,Group 内所有消费者必须停止消费,等待 Coordinator 完成重新分配。在分区数量多或消费者数量大时,这个过程可能持续数秒甚至数十秒。
**Rebalance 风暴**:当消费者处理消息过慢导致心跳超时,被踢出 Group 触发 Rebalance;Rebalance 期间积压更多消息;消费者重新加入后又因处理不过来被踢出——形成恶性循环。
### Static Membership 减少不必要的 Rebalance
Kafka 2.3 引入 Static Membership 机制:每个消费者配置固定的 `group.instance.id`。消费者短暂断开重连时,只要在 `session.timeout.ms` 内恢复,就不会触发 Rebalance。
这在容器化部署中特别有用——Pod 重启时不会引发整个 Consumer Group 的 Rebalance 风暴。
> [!question]
> Rebalance 期间消费者会暂停消费,在高并发场景下这会造成什么问题?如何缓解?
### Go 代码:Kafka Consumer 分区分配配置
```go
package main
import (
"github.com/segmentio/kafka-go"
)
func main() {
// 创建 Reader 时指定 Consumer Group 和分配策略
r := kafka.NewReader(kafka.ReaderConfig{
Brokers: []string{"localhost:9092"},
Topic: "orders",
GroupID: "order-service",
// 使用 Cooperative Sticky 分配策略,减少 Rebalance 迁移
GroupBalancers: []kafka.GroupBalancer{
kafka.CooperativeGroupBalancer{},
},
// 静态成员:Pod 重启时不触发 Rebalance
GroupInstanceID: "order-consumer-pod-0",
})
defer r.Close()
for {
msg, err := r.ReadMessage(context.Background())
if err != nil {
// 处理错误,注意 Rebalance 期间会返回特定错误
break
}
process(msg)
}
}
```
关键配置说明:
- `GroupBalancers` 设置分配策略,`CooperativeGroupBalancer` 对应 Cooperative Sticky。
- `GroupInstanceID` 设置静态成员 ID,同一个 Pod 重启后保持相同的 ID,避免触发不必要的 Rebalance。
## 关联笔记
- [[02-消息模型/3-MQ-消息模型|MQ 消息模型]]
- [[07-主流MQ对比/22-Kafka|Kafka]]
- [[05-可靠性保障/15-MQ-顺序性保障|MQ 顺序性保障]]
- [[10-监控与运维/37-MQ-消费积压治理|MQ 消费积压治理]]
- [[12-架构与实战/49-MQ-客户端连接管理|MQ 客户端连接管理]]