一、引言
传统消息系统通常从两个方向演化:一类偏消息队列,强调业务系统解耦、异步处理、削峰填谷;另一类偏日志流,强调持续写入、顺序消费、回放和流处理。随着企业实时数据规模扩大,系统同时面临多业务线共享集群、多租户隔离、跨机房容灾、海量 Topic、长期数据保留、冷热分层和云原生部署等问题。Pulsar 的设计目标正是把这些能力纳入一个统一的消息与流平台中,而不是让工程团队在多套系统之间反复拼接。
Pulsar 是一个多租户、高性能的服务间消息解决方案,使用发布订阅模型:生产者向 Topic 写入消息,消费者订阅 Topic、处理消息,并在处理完成后向 Broker 确认。Pulsar 会在订阅存在时保留未确认消息,直到消费者确认处理完成;如果消费失败,也可以触发消息重投递机制。
业务系统解耦
|
v
+-------------------+
| 消息与事件平台 |
+-------------------+
^ ^
| |
实时数据流 云原生多租户
回放/处理 隔离/扩展/容灾
Kafka、RocketMQ、RabbitMQ 等系统分别在不同场景中成熟可靠,但 Pulsar 的鲜明特征是:它从架构上把 Broker 做成相对无状态组件,把持久化消息交给 BookKeeper,并把租户、命名空间、Topic、订阅、跨集群复制等能力设计成平台级概念。这种设计使它更适合需要大规模 Topic、多团队共享、跨地域部署和长期数据保留的场景。
二、核心概念
Pulsar 使用发布订阅模型。Producer 负责向 Topic 发布消息,Consumer 通过 Subscription 订阅 Topic 并消费消息,消息处理完成后发送 acknowledgment,Broker 据此判断消息是否可以从系统中清理或继续保留。
Pulsar 的 Topic 名称通常体现多租户结构:
persistent://tenant/namespace/topic
其中 tenant 是租户, namespace 是命名空间,topic 是具体主题。租户是 Pulsar 中最基础的 Topic 分类单位;命名空间则是租户内的 Topic 逻辑分组,很多策略可以在命名空间级别统一配置,例如存储配额、消息 TTL、隔离策略等。
一个最小的消息流可以表示为:
+----------+ publish +---------+ dispatch +----------+
| Producer | ---------------------> | Topic | -------------------> | Consumer |
+----------+ +---------+ +----------+
|
v
+--------------+
| Subscription |
| cursor / ack |
+--------------+
Pulsar 消息本身不只是 payload,消息组成包括 value/data payload、key、properties、producer name、topic name、schema version、sequence ID、message ID、publish time、event time 等;sequence ID 还能用于消息去重,message ID 则在消息持久化后由 bookies 分配,表示消息在 ledger 中的位置。
三、架构原理
Pulsar 的架构可以从“实例、集群、Broker、BookKeeper、元数据存储”五个层次理解。一个 Pulsar instance 可以包含一个或多个 Pulsar clusters,集群之间可以复制数据;一个 Pulsar cluster 通常由一个或多个 Broker、一个 BookKeeper 集群和一个元数据存储集群组成。
Pulsar Instance
|
+--------------------+--------------------+
| |
Cluster A Cluster B
| |
+-------+--------+ +--------+-------+
| | | |
Brokers BookKeeper Brokers BookKeeper
| | | |
+-------+--------+ +--------+-------+
| |
Metadata Store Metadata Store
|
Configuration Store
跨集群配置、租户、命名空间、复制配置
Broker 是 Pulsar 的消息服务层。Broker 是无状态组件,主要运行 HTTP 服务和异步 TCP dispatcher:HTTP 服务提供管理 REST API 与 Topic lookup,dispatcher 则通过 Pulsar 的二进制协议处理数据传输。Producer 和 Consumer 与 Broker 建立连接,Broker 负责接收、分发、负载均衡消息,并与元数据存储、BookKeeper 协作。
BookKeeper 是 Pulsar 的持久化存储层。Pulsar 使用 Apache BookKeeper 存储消息,BookKeeper 提供分布式 write-ahead log 能力,将数据写入称为 ledger 的独立日志结构。BookKeeper 支持顺序数据的高效存储、entry 复制、失败情况下的 ledger 读一致性,以及通过增加 bookies 横向扩展容量和吞吐。
Pulsar 的关键架构优势来自 Broker 与存储解耦:
写入路径
Producer
|
v
+--------+ append entry +-------------+
| Broker | -----------------------> | BookKeeper |
+--------+ | Bookies |
| +-------------+
|
v
Metadata Store
Topic ownership / ledger metadata / load data
读取路径
Consumer
^
|
+--------+ read cache or ledger +-------------+
| Broker | <------------------------------ | BookKeeper |
+--------+ +-------------+
这种架构意味着 Broker 不需要长期持有消息数据本身,Topic 迁移、Broker 扩缩容、负载均衡时不必像“存储和服务绑定”的系统那样搬迁大量数据。BookKeeper 可以通过增加 bookies 立即扩展容量,且 bookies 能处理大量 ledgers 的并发读写。
元数据存储负责集群元数据与协调任务,例如 Topic 元数据、Schema、Broker 负载数据等。官方提供 Oxia、ZooKeeper、RocksDB 等后端, Oxia 推荐用于新集群,ZooKeeper 是具备强一致性保证的生产可用元数据存储,RocksDB 则适合 standalone 部署。
四、消息模型
Pulsar 支持多种订阅类型,包括 Exclusive 、 Shared 、 Failover 和 Key_Shared ,这使同一个 Topic 能够根据业务语义适配独占消费、共享并发消费、主备消费和按 key 有序消费等不同模式。
同一个 Topic,不同订阅语义
1. Exclusive
Topic -> Consumer A
Consumer B 无法同时加入同一订阅
2. Shared
Topic -> Consumer A
-> Consumer B
-> Consumer C
消息在多个消费者之间分发
3. Failover
Topic -> Consumer A 主
Consumer B 备
Consumer C 备
4. Key_Shared
key=user-1 -> Consumer A
key=user-2 -> Consumer B
相同 key 的消息尽量保持有序分发
确认机制是 Pulsar 消息可靠性的基础。消费者成功处理消息后向 Broker 发送 ack;Pulsar 支持 individual acknowledgment 和 cumulative acknowledgment。需要注意的是,cumulative acknowledgment 不能用于 Shared 或 Key_Shared 订阅,因为这些订阅类型中多个消费者共享同一订阅,消息应逐条确认。
在失败处理上,Pulsar 支持 negative acknowledgment、ack timeout、retry letter topic 和 dead letter topic 等机制。negative acknowledgment 的重投递计数只保存在内存中,Broker 重启、bundle unload/rebalance、topic unload 或消费者断开时会归零;如果业务需要可靠的最大重试次数,建议使用 reconsumeLater 配合 enableRetry(true) ,因为 retry count 会作为消息属性持久化。
五、功能特性
Pulsar 的第一类能力是平台化。它原生支持多集群 instance、跨集群 geo-replication、多租户、权限控制、命名空间策略和隔离策略。租户可以跨集群分布,并拥有自己的认证和授权方案;租户也是容量分配与策略管理的基本单元之一。
第二类能力是存储与扩展。Pulsar 使用 BookKeeper 提供持久化消息存储,并通过 ledger 和 managed ledger 抽象管理 Topic 的消息流。managed ledger 表示一个 Topic 的存储层,内部可以由多个 BookKeeper ledgers 构成,这样失败后可以创建新 ledger,也可以在所有 cursor 都消费完某个 ledger 后删除它。
第三类能力是冷热分层。Pulsar 的 Tiered Storage 利用 segment-based architecture,把已经 sealed、不可变的 ledger 从 BookKeeper offload 到外部低成本存储,例如 S3、GCS、Azure Blob、Alibaba Cloud OSS、文件系统或 S3 兼容存储;消费者读取已 offload 数据时,Broker 会根据元数据透明访问外部存储。
热数据路径
Producer -> Broker -> BookKeeper
冷数据路径
BookKeeper sealed ledger
|
v
+------------------+
| Tiered Storage |
| S3 / GCS / OSS |
| Azure / MinIO |
+------------------+
Consumer 读取历史数据时:
Consumer -> Broker -> BookKeeper metadata -> 外部存储
第四类能力是跨地域复制。Pulsar 的 geo-replication 在 namespace 级别管理,租户必须对目标集群拥有访问权限;消息先持久化到本地集群,再异步转发到远端集群。在正常网络条件下,消息通常会立即复制,端到端延迟通常由跨地域网络 RTT 决定。
本地写入 异步复制
+----------+ +------------+ +------------+
| Producer | --> | Cluster A | ----> | Cluster B |
+----------+ | local log | | remote log |
+------------+ +------------+
|
v
Local Consumer
注意:
- Topic 数据可复制
- 普通 subscription 是集群本地状态
- replicated subscription 只同步 mark-delete position
第五类能力是轻量流处理与连接器。Pulsar Functions 是运行在 Pulsar 之上的 serverless 计算框架,消费一个或多个输入 Topic,执行用户逻辑,再把结果写入输出 Topic、日志 Topic 或状态存储;支持 Java、Python 和 Go。Pulsar IO 则提供 Source 和 Sink 两类连接器,用于把外部系统数据写入 Pulsar,或把 Pulsar 数据写出到外部系统。
外部系统 -> Source Connector -> Pulsar Topic -> Function -> Pulsar Topic -> Sink Connector -> 外部系统
DB Kafka/File/etc raw-data 清洗转换 clean-data ES/HDFS/DB
六、特性优势
- 大规模多租户与多 Topic 场景。Pulsar 可以扩展到超过一百万个 Topic,并支持多租户、访问控制和资源隔离;这对于数据平台、IoT、用户行为采集、日志事件平台这类 Topic 数量多、业务团队多的场景很重要。
- 架构弹性。Broker 无状态、存储由 BookKeeper 负责,容量与吞吐可以通过增加 bookies 横向扩展;Broker 侧扩容更多偏服务能力,BookKeeper 侧扩容更多偏存储与 I/O 能力。
- 跨地域能力内建。Pulsar 的 geo-replication 不是外围同步脚本,而是平台能力,支持 namespace 级别配置,也支持 topic 级别配置;消息先写本地再异步复制,允许应用在任意集群生产和消费。
- 生态闭环。Pulsar 不只提供消息收发,还提供 Functions、IO connectors、Schema、Transactions、Tiered Storage 等能力,这使 Pulsar 可以从单纯消息中间件扩展为实时数据基础设施。
七、适用场景
Pulsar 适合用作企业级消息中台。多业务线、多团队、多环境共享一套消息平台时,可以通过 tenant、namespace、authorization、quota、TTL、isolation policies 等机制做隔离与治理;Topic URL 天然体现租户与命名空间结构,便于平台化管理。
它也适合实时数据管道。通过 Pulsar IO 的 Source/Sink,外部数据库、消息系统和数据服务可以与 Pulsar 打通;通过 Pulsar Functions,可以在消息流中加入轻量清洗、路由、格式转换和简单聚合逻辑,而不一定为每个轻处理场景单独部署复杂流处理引擎。
跨地域容灾和异地多活也是 Pulsar 的典型方向。geo-replication 可以在 namespace 或 topic 级别配置复制集群,消息本地持久化后异步转发到远端;但如果要做消费位点容灾,需要关注 replicated subscription 的限制,因为普通 subscription 并不会作为全局状态自动同步。
长期数据保留、事件回放、审计追踪也适合 Pulsar。Tiered Storage 可以将老的 backlog 从 BookKeeper 转移到更便宜的长期存储,同时让客户端像读取 BookKeeper 中的数据一样透明访问这些历史消息;这对合规留存、历史分析、审计数据保留有现实意义。
不太适合的场景也要明确。如果只是单机小规模任务队列,或者团队没有多租户、跨地域、长期保留、海量 Topic 等需求,Pulsar 的组件复杂度可能超过收益。生产级 Pulsar 通常涉及 Broker、BookKeeper、元数据存储、Proxy、监控、权限、安全、容量规划,运维门槛高于轻量 MQ。
八、部署使用
本地体验可以用 standalone 或 Docker Compose 快速启动,生产和类生产环境更常见的是 Kubernetes + Helm。典型 Kubernetes 部署流程如下:
准备 Kubernetes
|
v
添加 Helm repo
|
v
准备 Helm release secret
|
v
helm install Pulsar
|
v
检查 pods / services
|
v
创建 tenant / namespace / topic
|
v
生产与消费测试
|
v
接入 Prometheus / Grafana / Manager
九、落地建议
如果是第一次在团队中引入 Pulsar,建议先从“消息平台”而不是“替换 Kafka”切入。优先选择多租户明显、Topic 数量多、需要统一权限和命名空间治理、需要长期保留或跨地域复制的业务试点,这些场景更容易体现 Pulsar 的架构收益。
容量规划时要分开看 Broker、BookKeeper 和元数据存储。Broker 关注连接数、Topic ownership、消息分发、CPU 和网络;BookKeeper 关注写入吞吐、磁盘 IOPS、ledger 数量、journal 与 ledger 目录隔离;元数据存储关注一致性、可用性和元数据规模。Pulsar 的复杂度不是缺点本身,但如果没有监控、限流、权限和命名规范,复杂度会变成运维风险。
命名规范建议从第一天建立:
persistent://{tenant}/{namespace}/{topic}
tenant:
按组织、业务域或平台租户划分
namespace:
按应用、环境、数据域划分
topic:
建议包含业务对象、事件类型、版本
例如:
persistent://payment/prod/order-paid-v1
persistent://iot/device/device-status-v1
订阅类型也要按语义选择,而不是默认全部 Shared:
| 场景 | 推荐订阅类型 | 原因 |
|---|---|---|
| 单实例严格顺序处理 | Exclusive | 同一订阅只允许一个消费者 |
| 多实例并发消费 | Shared | 提升吞吐,但不强调全局顺序 |
| 主备消费 | Failover | 主消费者处理,失败后备消费者接管 |
| 按业务 key 保序并发 | Key_Shared | 相同 key 的消息尽量由同一消费者处理 |
十、常见问题
- 为什么说 Broker 是无状态的?
这里的“无状态”不是指 Broker 完全不参与状态管理,而是指消息持久化不主要依赖 Broker 本地磁盘。Broker 负责协议接入、Topic lookup、消息分发、负载均衡、与元数据存储和 BookKeeper 协作;消息数据持久化在 BookKeeper 中,订阅 cursor 也持久化在 BookKeeper 中。
- Pulsar 和 Kafka 最大差异是什么?
从入门理解看,最关键差异是 Pulsar 的服务层和存储层天然解耦:Broker 负责服务,BookKeeper 负责持久化日志;Kafka 的经典模型中,Broker 同时承担服务与分区数据存储。这个差异会影响扩缩容、Topic 数量、多租户隔离、存储扩展和运维模型。
- Pulsar 的订阅状态能跨集群同步吗?
普通 subscription 是集群本地状态,只有 replicated subscription 可以同步 mark-delete position,并且该能力主要面向 failover,不是 active-active 消费。
- 使用 nack 能可靠进入死信队列吗?
不一定,negative acknowledgment 的 redelivery counter 只保存在内存中,Broker 重启、bundle unload/rebalance、topic unload 或 consumer disconnect 都会让计数归零;建议使用 reconsumeLater 与 enableRetry(true) ,因为 retry count 会作为消息属性持久化。
- Tiered Storage 会影响读取性能吗?
会,尤其是首次读取冷数据时。读取 offloaded data 会因为网络延迟比 BookKeeper 慢,一些实现可能会缓存频繁访问的数据,Broker 也可能基于访问模式预取;写入方面,offloading 通常在后台异步执行,不直接影响写入路径。