Apache Pulsar 实战:云原生消息队列与多租户流平台

Apache Pulsar 是新一代云原生消息队列与流处理平台,采用计算与存储分离的架构,天然支持多租户、跨地域复制和海量 Topic。相比 Kafka 的单层设计,Pulsar 用 BookKeeper 做持久化存储、Broker 无状态转发,既扛得住高吞吐消息队列场景,也能长期保留事件流做回放。本文从单机部署一路讲到订阅模式、多租户隔离与生产落地清单。

在订单、日志、IoT 埋点这类场景里,Pulsar 既能当异步解耦的缓冲区,也能当事件溯源的持久化日志。一个常见的落地形态是:上游服务把领域事件发到 Pulsar,下游的履约、风控、数仓各自用独立订阅消费同一份数据,彼此不耦合,新增消费者也无须改动生产者。

一、为什么需要 Pulsar:消息队列与流平台的合体

传统消息中间件里,Kafka 偏向”日志流”、RabbitMQ 偏向”任务队列”,两者能力边界明显。Pulsar 的出现把两条线合到了一起:同一份数据既能像队列一样被多个消费者组并发消费,也能像事件流一样长期保留、随时回放。它把”服务层(Broker)”和”存储层(BookKeeper)”拆开,Broker 变成无状态路由节点,扩缩容时不需要像 Kafka 那样搬移分区数据,这正是它被称为云原生的根本原因。

二、核心架构:Broker、BookKeeper 与 ZooKeeper 三层

Pulsar 的架构由三部分组成:Broker 负责接收生产者的消息、分发给消费者,本身不存数据;BookKeeper 是一组独立的存储节点(Bookie),消息以 ledger 形式追加写入、多副本持久化;ZooKeeper / 元数据服务 保存 Topic 路由、租户与命名空间等元数据。这种分层让”加 Broker 只扩计算、加 Bookie 只扩存储”成为可能,运维弹性明显优于单层架构。

三、快速起一个集群:用 Docker 跑单机版

学习和验证阶段直接用官方 standalone 镜像最省事,一条 compose 就能把 6650(二进制协议)和 8080(管理 API)两个端口暴露出来:

version: "3.8"
services:
  pulsar:
    image: apachepulsar/pulsar:3.3.0
    command: bin/pulsar standalone
    ports:
      - "6650:6650"   # Pulsar 二进制协议端口
      - "8080:8080"   # Admin REST API 端口
    environment:
      - PULSAR_MEM="-Xms512m -Xmx512m"

启动后执行 bin/pulsar-admin brokers list 能看到 standalone 节点即表示集群就绪。生产环境则要把 Broker、BookKeeper、元数据服务拆成独立部署,并配置多副本防止单点。

四、生产消费:用官方客户端收发消息

Pulsar 客户端覆盖 Java、Python、Go、Node 等主流语言,语义高度统一。下面用 Java 客户端演示向一个持久化 Topic 发送消息:

import org.apache.pulsar.client.api.*;

PulsarClient client = PulsarClient.builder()
        .serviceUrl("pulsar://localhost:6650")
        .build();

Producer<String> producer = client.newProducer(Schema.STRING)
        .topic("persistent://my-tenant/my-ns/orders")
        .create();

producer.send("order-1001 created");
producer.close();
client.close();

消费者需要指定”订阅名”和”订阅类型”,这是 Pulsar 与 Kafka 消费者组最大的不同——同一个 Topic 下可以并存多种订阅:

Consumer<String> consumer = client.newConsumer(Schema.STRING)
        .topic("persistent://my-tenant/my-ns/orders")
        .subscriptionName("order-processor")
        .subscriptionType(SubscriptionType.Shared)
        .subscribe();

Message<String> msg = consumer.receive();
System.out.println("收到: " + msg.getValue());
consumer.acknowledge(msg.getMessageId());

Python 侧同样简洁,适合脚本或数据管道接入:

import pulsar

client = pulsar.Client('pulsar://localhost:6650')
producer = client.create_producer('persistent://my-tenant/my-ns/orders')
producer.send(('order-1002 created').encode('utf-8'))
client.close()

五、四种订阅模式:独占、共享、故障转移、Key_Shared

订阅类型决定了消息如何在消费者之间分配,是设计消费逻辑时最先要拍板的事:

订阅模式并发消费消息顺序典型场景
Exclusive 独占仅 1 个消费者严格有序单实例顺序处理
Failover 故障转移主 1 + 备 N分区内有序主备高可用
Shared 共享多消费者轮询不保证顺序任务队列、水平扩展
Key_Shared 按 Key多消费者相同 Key 有序按用户/订单分片

实际项目里,Shared 适合做削峰填谷的任务队列,Key_Shared 能在多实例并发的同时保证同一业务主键(如订单 ID)的消息落到同一消费者,兼顾吞吐与顺序。想系统对比消息中间件的选型,可以参考本站 Kafka 入门、RabbitMQ 实战 与 NATS 实战。

六、多租户:Tenant、Namespace 与资源隔离

Pulsar 原生把”租户(Tenant)→ 命名空间(Namespace)→ Topic”做成三层,一个集群可以安全地服务多个团队甚至多个客户。用 pulsar-admin 几行命令就能建出隔离单元:

bin/pulsar-admin tenants create shop
bin/pulsar-admin namespaces create shop/order
bin/pulsar-admin topics create-partitioned-topic shop/order/events -p 4
bin/pulsar-admin namespaces set-retention shop/order   --size 100M --time 7d

通过 Namespace 级别的策略,可以分别设置 retention(保留时长)、backlog 配额、鉴权和限流,不同租户之间互不干扰。这对 SaaS 厂商或中台团队尤其友好,不用为每个业务单独搭一套集群。

七、与 Kafka 怎么选:一张表说清

维度Apache PulsarApache Kafka
架构计算存储分离(Broker+BookKeeper)单层(Broker 自带日志)
扩容加 Broker/Bookie 互不影响扩分区需搬移数据
多租户原生 Tenant/Namespace靠 ACL + 约定
订阅模型四种,可并存消费组一种
消息确认单条 ack + 重试/死信按偏移提交
运维复杂度组件多,初期偏重组件少,生态成熟

一句话结论:已有深厚 Kafka 生态、追求极致吞吐且团队熟练,继续 Kafka 更省心;若需要多租户隔离、灵活订阅、存算分离弹性,或想在同一平台同时满足队列与流,Pulsar 更合适。和 Redis Streams 这类轻量方案比,Pulsar 胜在规模与治理能力。

另外要注意团队成熟度:Pulsar 的组件更多,初期运维心智负担比 Kafka 重,建议在真正需要多租户或存算分离弹性时再引入,避免为了用而用。若当前只是简单的异步通知,RabbitMQ 这类更轻量的队列往往更快上手。

八、可靠投递:重试、死信与确认机制

生产环境消费必然遇到偶发失败。Pulsar 支持在 Namespace 或 Topic 维度配置重试队列与死信队列(DLQ),超过最大重试次数后消息自动转入 DLQ 供人工排查,不会阻塞主流程:

bin/pulsar-admin topics set-dead-letter-policy   -m 3   -dlq persistent://shop/order/events-dlq   persistent://shop/order/events

配合消费端的单条 acknowledge,即使进程崩溃未确认的消息也会被重新投递,从而保证”至少一次”语义。需要精确一次(Exactly-Once)时,可借助 Pulsar 的事务消息能力。

九、生产落地检查清单

检查项建议做法
部署形态生产用独立 Broker + BookKeeper + 元数据服务,禁用 standalone
副本与 ackBookKeeper ensemble 写 3 副本,生产者用 WaitForAll 级确认
订阅类型按业务选 Shared/Key_Shared,避免误用独占导致堆积
死信与重试务必配置 DLQ,失败消息隔离不堵主链路
监控接入 Prometheus 看吞吐、积压、存储延迟
容量合理设置 retention 与 backlog 配额,防磁盘打满
发布集成部署与冒烟纳入 GitHub Actions 流水线

十、小结

Apache Pulsar 用存算分离的架构把”消息队列”和”事件流”统一到一个平台,原生多租户与四种订阅模式让它在复杂业务里游刃有余。从 Docker 单机起步,到生产环境的副本、死信、监控与 CI 集成,只要按上面的清单逐项落地,就能把这套云原生消息底座真正用稳。当你的消息场景开始要求多团队隔离和弹性扩缩时,Pulsar 会是一个值得长期投入的选择。

上一篇 ShardingSphere 实战:分库分表与读写分离落地
下一篇 Spring Data JPA 实战:从 CRUD 到关联映射与分页查询
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]