每天认识一个组件:Apache Pulsar

0 阅读12分钟

一、引言

传统消息系统通常从两个方向演化:一类偏消息队列,强调业务系统解耦、异步处理、削峰填谷;另一类偏日志流,强调持续写入、顺序消费、回放和流处理。随着企业实时数据规模扩大,系统同时面临多业务线共享集群、多租户隔离、跨机房容灾、海量 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 通常在后台异步执行,不直接影响写入路径。