Pulsar Subscription(订阅) 是消费者消费消息的核心机制,决定了消息如何在多个消费者之间分发。Pulsar 提供了 四种主要的订阅模式(Subscription Types),每种模式适用于不同的业务场景,支持灵活的消息语义(如队列、广播、顺序处理等)。
📘 Pulsar Subscription 模式详解
一、订阅的基本概念
1. 什么是 Subscription?
- Subscription 是消费者对某个 Topic 的“订阅关系”。
- 每个订阅有一个唯一的名称(如
sub1),并维护一个独立的 消费游标(Cursor),记录当前消费到哪一条消息。 - 一个 Topic 可以被多个不同的 Subscription 订阅,彼此互不影响(广播语义)。
- 消费者通过订阅来接收消息。
Topic: orders-topic
├─ Subscription: sub-a (Exclusive) → Consumer-A
├─ Subscription: sub-b (Shared) → Consumer-B, Consumer-C
└─ Subscription: sub-c (Failover) → Primary: D, Backup: E
💡 游标(Cursor)持久化在 BookKeeper 中,即使消费者重启也能从上次位置继续消费。
二、四种订阅模式详解
Pulsar 支持以下四种核心订阅模式:
| 模式 | 英文名 | 适用场景 |
|---|---|---|
| 1. 独占模式 | Exclusive | 单消费者,高可靠性 |
| 2. 共享模式 | Shared / Round Robin | 多消费者负载均衡(队列语义) |
| 3. 主备模式 | Failover | 消费者主备切换,保证顺序 |
| 4. Key 共享模式 | Key_Shared | 按消息 Key 分配,保证 Key 级顺序 |
1. Exclusive(独占模式)
✅ 特点:
- 只能有一个消费者 订阅该 Subscription。
- 如果有第二个消费者尝试连接,会收到
SubscriptionBusyException。 - 消息按顺序发送给唯一消费者。
- 保证 严格的消息顺序 和 恰好一次(Exactly-Once) 语义的可能。
🔧 使用场景:
- 配置管理、定时任务、日志聚合等需要单一处理者的场景。
🚫 限制:
- 无法水平扩展消费能力。
- 单点故障风险。
示例代码(Java):
Consumer<byte[]> consumer = client.newConsumer()
.topic("orders-topic")
.subscriptionName("sub-exclusive")
.subscriptionType(SubscriptionType.Exclusive)
.subscribe();
2. Shared(共享模式 / 轮询模式)
✅ 特点:
- 允许多个消费者 同时订阅同一个 Subscription。
- 消息通过 轮询(Round Robin) 方式分发给消费者,实现负载均衡。
- 不保证消息顺序(除非单个消费者)。
- 每个消息只被一个消费者处理(队列语义)。
⚠️ 注意:
- 消费者可以动态加入或退出。
- Broker 会根据消费者的接收能力(接收队列大小)进行流量控制。
🔧 使用场景:
- 高吞吐、可并行处理的业务,如订单处理、图片转码、事件分发。
示例代码(Java):
Consumer<byte[]> consumer = client.newConsumer()
.topic("orders-topic")
.subscriptionName("sub-shared")
.subscriptionType(SubscriptionType.Shared)
.subscribe();
📌 提示:Shared 模式下,未确认的消息(Unacked)会堆积在 Broker,直到被 Ack 或超时重发。
3. Failover(主备模式 / 故障转移)
✅ 特点:
- 多个消费者订阅,但 只有一个“主消费者”(Leader) 接收消息。
- 其他消费者处于“待命”状态。
- 当主消费者断开时,Broker 自动将下一个消费者提升为新的主消费者。
- 所有消费者按名字排序,确定主备顺序(字典序)。
🔧 使用场景:
- 需要 保证消息顺序 且 高可用 的场景,如状态机更新、金融交易回放。
🎯 优势:
- 既保证顺序性,又具备容错能力。
- 适合不能并行处理的业务逻辑。
示例代码(Java):
Consumer<byte[]> consumer = client.newConsumer()
.topic("state-events")
.subscriptionName("sub-failover")
.subscriptionType(SubscriptionType.Failover)
.consumerName("consumer-1") // 名字决定优先级
.subscribe();
消费者命名建议:
consumer-0,consumer-1,consumer-2,确保排序正确。
4. Key_Shared(Key 共享模式)
✅ 特点:
- 多个消费者共享订阅。
- 消息根据 消息 Key(Message Key) 的哈希值分配给特定消费者。
- 相同 Key 的消息总是由同一个消费者处理,保证 Key 级别的顺序性。
- 不同 Key 的消息可并行处理,提升吞吐。
🔧 使用场景:
- 用户行为分析、订单状态更新、会话跟踪等需要“按用户/订单 ID 顺序处理”的场景。
🎯 优势:
- 兼顾 高吞吐 与 局部顺序性。
示例代码(Java):
Producer<byte[]> producer = client.newProducer()
.topic("user-events")
.messageRoutingMode(MessageRoutingMode.SinglePartition)
.hashingScheme(HashingScheme.JavaStringHash)
.create();
Consumer<byte[]> consumer = client.newConsumer()
.topic("user-events")
.subscriptionName("sub-key-shared")
.subscriptionType(SubscriptionType.Key_Shared)
.keySharedPolicy(KeySharedPolicy.stickyHash())
.subscribe();
Key_Shared 策略(Policy):
auto_split:自动分裂负载,动态调整 Key 分配。sticky_hash:使用哈希将 Key 分配到消费者,减少重分配。
⚠️ 注意:Producer 必须为每条消息设置
key,否则无法正确路由。
producer.newMessage()
.key("user-123") // 必须设置 Key
.value("clicked")
.send();
三、订阅模式对比表
| 模式 | 消费者数量 | 消息顺序 | 吞吐能力 | 容错性 | 典型场景 |
|---|---|---|---|---|---|
| Exclusive | 1 | ✅ 全局顺序 | ❌ 低 | ❌ 无 | 单实例任务 |
| Shared | N | ❌ 无序 | ✅ 高 | ✅ 高 | 并行处理队列 |
| Failover | N(主备) | ✅ 全局顺序 | ⚠️ 中等 | ✅ 高 | 顺序敏感 + HA |
| Key_Shared | N | ✅ Key 级顺序 | ✅ 高 | ✅ 高 | 分区顺序处理 |
四、高级订阅功能
1. 共享模式下的 Ack 机制
- 消费者必须显式调用
acknowledge()确认消息。 - 支持批量 Ack、Negative Ack(nack)重试。
consumer.acknowledge(msg); // 正常确认
consumer.negativeAcknowledge(msg); // 立即重试
2. 共享模式的 Backlog 控制
- Broker 为每个消费者维护未确认消息队列。
- 可设置
receiverQueueSize控制预取数量,防止消费者过载。
3. 死信队列(DLQ)集成
- 所有订阅模式都支持配置 DLQ,失败消息自动转入死信 Topic。
pulsar-admin namespaces set-dead-letter-policy my-namespace \
--maxRedeliverCount 5 \
--deadLetterTopic my-dlq-topic
4. 重置消费位点(Replay)
- 可将订阅游标重置到任意时间点,重新消费历史消息。
pulsar-admin topics reset-cursor -s sub1 -t "2024-01-01T00:00:00Z" my-topic
五、最佳实践建议
| 实践 | 建议 |
|---|---|
| 📌 命名规范 | 订阅名应体现用途,如 order-processor, audit-log |
| 📌 避免重复订阅名 | 同一命名空间下不能有两个同名订阅 |
| 📌 Shared 模式监控 Backlog | 关注未确认消息堆积,防止内存溢出 |
| 📌 Key_Shared 模式设置 Key | Producer 必须设置 Key,否则路由失效 |
| 📌 Failover 模式命名有序 | 使用 consumer-0, consumer-1 确保主备顺序 |
| 📌 权限控制 | 通过 Namespace 授权订阅权限 |
六、可视化示例
Topic: user-actions
│
├── Subscription: sub-batch (Exclusive)
│ └── Batch-Processor (唯一消费者)
├── Subscription: sub-webhook (Shared)
│ ├── Webhook-Sender-1
│ ├── Webhook-Sender-2
│ └── Webhook-Sender-3 → 轮询分发
├── Subscription: sub-recovery (Failover)
│ ├── Primary: Recovery-Service-A
│ └── Backup: Recovery-Service-B → 主备切换
└── Subscription: sub-analytics (Key_Shared)
├── Analyzer-UserGroup-A ← Key: user-100, user-101
├── Analyzer-UserGroup-B ← Key: user-200, user-201
└── Analyzer-UserGroup-C ← Key: user-300
✅ 总结
| 订阅模式 | 核心价值 |
|---|---|
| Exclusive | 简单、顺序、单处理者 |
| Shared | 高吞吐、负载均衡、队列语义 |
| Failover | 顺序 + 高可用,主备切换 |
| Key_Shared | 并行 + Key 级顺序,最灵活 |
📌 一句话总结:
选择正确的订阅模式,是构建高效、可靠消息系统的基石。根据业务是否需要顺序、并发、容错,合理选择
Exclusive、Shared、Failover或Key_Shared。
43

被折叠的 条评论
为什么被折叠?



