快递行业有一条铁律:分拣台再快,库房不够也会爆仓;库房再大,分拣台人手不足也会积压。Apache Pulsar 就像一个把分拣台和立体仓库彻底分开的现代物流园——分拣台(Broker)只负责接单和派单,包裹真正的存放全交给独立仓库(BookKeeper)。Kafka 则是老式仓配一体楼,货架、分拣台挤在同一个空间,扩容计算往往要搬走数据。Pulsar 靠这种架构,天然获得秒级扩容、多租户、多订阅模式、分层存储、跨地域复制等能力,成为流处理消息队列的新宠。

岗位关联

学会本讲,你将理解 Pulsar 为什么比 Kafka 更适合云原生场景,并能在日志管道、实时特征、事件驱动架构中做出有说服力的技术选型。

目录
  1. 可视化演示
  2. 架构革新
  3. 弹性扩容
  4. 多租户隔离
  5. 订阅模式
  6. 数据可靠性
  7. 分层存储
  8. 跨域复制
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示Pulsar核心优势:计算存储分离架构、四种订阅模式、与Kafka对比选型。切换模式查看不同维度。

本节配有交互动效(进入视口后自动加载,约 3KB)。

架构革新

快递物流园里,决定每天能处理多少包裹的,是分拣台和仓库两个环节的短板。传统 Kafka 像一座“仓配一体”老楼——货架、分拣台挤在同一层,想多加一台分拣机,得先搬走楼里的货。Pulsar 则把物流园拆成两个独立系统:分拣台(Broker)只负责接单和派单,立体仓库(BookKeeper)只负责存包裹。这种“计算存储分离”的架构,是 Pulsar 一切优势的地基。

在 Pulsar 中,Broker 是无状态服务,不保存任何消息数据;消息以 segment 形式写入 BookKeeper 的 ledger 中。消费者要从哪个 topic 读,Broker 临时去仓库取,读完数据仍留在仓库。Kafka 的每个 broker 必须带着分区副本,所以扩缩容总伴随数据迁移。

这样一来,你不用担心仓库放不下还要给所有分拣台搬家;哪边缺,扩哪边,这就是 Pulsar 架构革新的价值。

Ttotal=min⁡(B×tb,  K×ts)\displaystyle T_{\text{total}}=\min\left(B\times t_b,\;K\times t_s\right)
公式说明:整体吞吐由计算层和存储层中较小的那个决定,这也是为什么要两套系统分开扩容。
  1. B×t_bBroker 数量乘以单个 Broker 吞吐,代表分拣台的总处理能力。
    B×tb\displaystyle B\times t_b
  2. K×t_sBookKeeper 节点数乘以单节点存储吞吐,代表仓库的吞吐上限。
    K×ts\displaystyle K\times t_s
  3. min(·)整个系统最终吞吐等于两个上限中较小的值,瓶颈在哪边就先扩哪边。
    min⁡(x,y)\displaystyle \min(x,y)

弹性扩容

双十一快到了,分拣台不够用,你只需要在空地上搭几张新分拣台。在 Pulsar 中,新 Broker 启动后从元数据服务认领 topic,不需要复制任何存量数据,所以加机器以秒计。Kafka 加 broker 时需要重新分配 partition 和副本,数据搬迁往往以小时计。

Pulsar 的存储层缩容同样灵活。因为数据在 BookKeeper 里,Bookie 节点可以逐步下线并让 ledger 自动重新分布。计算层与存储层互相不绑架,弹性伸缩自然更快。

物流园的双十一不再可怕:先加分拣台顶住峰值,再去扩仓库容量,两边互不拖累。

ΔT=ΔB×tb,if K×ts>(B0+ΔB)×tb\displaystyle \Delta T=\Delta B\times t_b,\quad \text{if } K\times t_s > (B_0+\Delta B)\times t_b
公式说明:当存储层没有成为瓶颈时,新增计算资源带来的吞吐收益几乎是线性的。
  1. ΔB新增的 Broker 数量,相当于多出来的分拣台。
    ΔB\displaystyle \Delta B
  2. t_b单个 Broker 的吞吐,相当于每张分拣台的处理速度。
    tb\displaystyle t_b
  3. if 条件只有当存储层余量充足时,新增分拣台才能真正提升整体吞吐。
    K×ts>(B0+ΔB)×tb\displaystyle K\times t_s > (B_0+\Delta B)\times t_b

多租户隔离

大型物流园不会让所有快递公司共用同一个库区,而是给每家租户划分独立库区,货架、道闸、月台分开管理。Pulsar 的多租户模型也这样:tenant 是快递公司,namespace 是库区,topic 是货道。管理员可以为每个 namespace 设置存储配额、消息速率和权限。

一台 Pulsar 集群可以同时服务多个业务团队,每个团队只看得见自己的租户。某个团队流量暴涨时,最多触到自己的配额上限,不会挤爆邻居的库区。Kafka 要实现类似隔离,通常需要为每个团队单独铺集群,运维成本高出一大截。

库区门禁管好了,几十个团队共用一套物流园也不会互相串货。这就是多租户隔离的价值。

Ui=usediquotai≤1\displaystyle U_i=\frac{\text{used}_i}{\text{quota}_i}\le 1
公式说明:每个租户的资源使用率必须不超过 1,超过就限流或拒绝,从而保证互不干扰。
  1. used_i第 i 个租户当前已使用的资源量,比如存储、带宽或消息速率。
    usedi\displaystyle \text{used}_i
  2. quota_i管理员给第 i 个租户设定的配额上限。
    quotai\displaystyle \text{quota}_i
  3. ≤1使用率不超过 1 时一切正常,超过 1 就触发限流。
    usediquotai≤1\displaystyle \frac{\text{used}_i}{\text{quota}_i}\le 1

订阅模式

同一个 topic 里的消息,可以像包裹一样有不同的取件方式。独占订阅是只请一个人签收;故障转移是正选签收人不在时备选人顶上;共享订阅是十个人一起拆包裹,谁有空谁拿;KeyShared 是按收货地址分人,同一个地址永远由同一个人负责,保证同一 key 的消息不乱序。

多订阅模式让 Pulsar 比 Kafka 灵活得多。Kafka 的消费组本质上更像故障转移加共享的混合体,但无法在同一份数据上按需切换。Pulsar 在消费端可以随时创建不同订阅,互不影响。

取件方式可以按需换:要严格单播就独占,要高吞吐就共享,要保序又高吞吐就 KeyShared,一个系统全部搞定。

Rshared=∑i=1nri,Rkey_shared=∑j=1krj\displaystyle R_{\text{shared}}=\sum_{i=1}^{n}r_i,\qquad R_{\text{key\_shared}}=\sum_{j=1}^{k}r_j
公式说明:共享订阅的总吞吐是多个消费者速率之和;KeyShared 在保证 key 有序的前提下,也能把整体吞吐摊到多个消费者上。
  1. r_i第 i 个消费者在共享订阅中的消费速率。
    ri\displaystyle r_i
  2. 求和共享订阅总吞吐等于所有消费者速率之和,人数越多,吞吐越高。
    ∑i=1nri\displaystyle \sum_{i=1}^{n}r_i
  3. key_sharedKeyShared 按 key 分配,不同 key 可以由不同消费者并行处理。
    ∑j=1krj\displaystyle \sum_{j=1}^{k}r_j

数据可靠性

仓库收货时不是把包裹随手一放就告诉商家“已签收”,而是同时放进好几个货架,至少有两个货架回复“放好了”,商家才收到成功回执。BookKeeper 的 ack quorum 就是这个“至少两个”的签收阈值。

Pulsar 的每个消息条目会写入 write quorum 个 Bookie 节点,拿到 ack quorum 个节点的确认后才告诉生产者成功。只要故障节点数不超过 ack quorum 减一,至少还有一个已确认的副本存活,消息就不会丢。

确认签收机制让物流园不必等所有货架都回话才告诉商家,既保证不丢货,又保住响应速度。

fmax⁡=Qa−1\displaystyle f_{\max}=Q_a-1
公式说明:允许同时故障而不丢数据的节点数等于 ack quorum 减一。比如 ack quorum 为 2,最多容忍 1 个节点故障。
  1. Q_aack quorum,即至少需要多少个副本回复“已写入”才算成功。
    Qa\displaystyle Q_a
  2. Q_a-1因为已经确认的副本有 Qa 份,所以允许其中有 1 份丢失,仍至少有 1 份安全。
    Qa−1\displaystyle Q_a-1
  3. f_max故障容忍上限,超过这个数就可能丢数据。
    fmax⁡\displaystyle f_{\max}

分层存储

仓库容量有限,把几个月前的包裹打包送去地下档案室,取件时再按编号流式调取。Pulsar 的分层存储把老 segment 卸载到 S3/HDFS,BookKeeper 只保留热数据,topic 的保留时间从“几周”变成“长期甚至无限”。

分层存储对 AI 场景尤其有用。训练样本需要回溯 90 天甚至更久,把 90 天前的数据全放 BookKeeper 成本太高。Pulsar 自动把冷 segment 上传对象存储,消费者依然能从同一个订阅里读历史消息,不需要重新搭一套离线管道。

档案室解决了仓库爆仓问题,历史消息不再需要忍痛删除,回溯窗口由成本决定,而不是由架构决定。

Rtotal=Shot+Scoldvwrite\displaystyle R_{\text{total}}=\frac{S_{\text{hot}}+S_{\text{cold}}}{v_{\text{write}}}
公式说明:消息可保留的时间等于热存储加冷存储的总量,除以每天的写入速度。
  1. S_hotBookKeeper 热存储层能容纳的数据量。
    Shot\displaystyle S_{\text{hot}}
  2. S_cold对象存储冷层能容纳的数据量,通常近乎无限。
    Scold\displaystyle S_{\text{cold}}
  3. v_write每天新写入的数据速度。
    vwrite\displaystyle v_{\text{write}}

跨域复制

一个物流园不够,在全国建两个园,面单数据异步同步。Pulsar 的跨地域复制也是异步的:北京写一条消息,后台异步复制到上海。消费者在两个地域都能读到副本,实现多活和容灾。

跨地域复制让 AI 平台可以“就近读写”。例如北京和上海各部署推理服务,日志各自写入本地 Pulsar,再互相异步复制;某个地域故障时,另一个地域的消费者还能继续消费相同的数据。

双物流园互相同步,任何一边台风停电,另一边都能继续运转,这就是跨地域复制的底气。

RPO≤Δrepl\displaystyle RPO \le \Delta_{\text{repl}}
公式说明:跨地域容灾最多丢失的数据窗口约等于复制延迟,网络越快、延迟越小,RPO 越低。
  1. RPO灾难恢复目标,代表最多允许丢失多长时间的数据。
    RPO\displaystyle RPO
  2. Δ_repl两个地域之间的异步复制延迟。
    Δrepl\displaystyle \Delta_{\text{repl}}
  3. ≤能丢失的数据不会超过最近一次复制未完成的时间窗。
    RPO≤Δrepl\displaystyle RPO \le \Delta_{\text{repl}}

AI 应用场景

互联网日志与训练数据管道

阶段:数据接入与清洗

解决的问题:传统 Kafka 在日志量暴涨时,扩 broker 往往触发数据搬迁,AI 团队等不起;Pulsar 计算存储分离后,日志峰值只需快速加 Broker,历史日志用分层存储放到对象存储,成本大幅下降。

具体案例:某推荐系统团队每天产生 50 TB 行为日志,用 Pulsar 同时服务实时特征与离线训练,夜间自动把 7 天前数据卸载到 S3,存储成本下降约 60%。

实时特征服务

阶段:特征工程与在线推理

解决的问题:在线推荐模型要求同一个 user 的特征事件严格有序,但流量大时单消费者扛不住;Pulsar 的 KeyShared 模式按 user_id 路由,同一 key 永远发到同一消费者,既保序又横向扩展。

具体案例:某广告模型以 user_id 为 key 写入 Pulsar,消费端用 KeyShared 订阅拉起 10 个消费者,实时特征更新延迟保持在百毫秒级,且同用户事件不乱序。

多地域模型服务容灾

阶段:模型部署与监控

解决的问题:跨地域部署模型服务后,推理日志需要汇聚到统一链路做监控和复盘;Pulsar 跨地域复制让两地数据异步同步,单地域故障时仍可保证日志不长期断流。

具体案例:某 AI 公司在北京、上海双活部署推理集群,请求日志分别写入本地 Pulsar 后互相同步;上海机房故障时,北京仍能继续消费全部日志做线上监控。

Python 演示

python51 行
import random  class Bookie:    def __init__(self, bookie_id):        self.bookie_id = bookie_id        self.entries = {}     def append(self, entry_id, data):        self.entries[entry_id] = data     def read(self, entry_id):        return self.entries.get(entry_id)  class MiniBroker:    def __init__(self, bookies, write_quorum=2, ack_quorum=2):        self.bookies = bookies        self.write_quorum = write_quorum        self.ack_quorum = ack_quorum        self.entry_id = 0     def produce(self, data):        chosen = random.sample(self.bookies, self.write_quorum)        self.entry_id += 1        acks = 0        for bookie in chosen:            bookie.append(self.entry_id, data)            acks += 1        if acks >= self.ack_quorum:            return self.entry_id        raise RuntimeError("ack quorum not reached")     def read(self, entry_id):        for bookie in self.bookies:            value = bookie.read(entry_id)            if value is not None:                return value        return None  def main():    bookies = [Bookie(i) for i in range(3)]    broker = MiniBroker(bookies)    entry_id = broker.produce("hello pulsar")    print("produced entry:", entry_id)    print("message:", broker.read(entry_id))  if __name__ == "__main__":    main()
目的:用纯 Python 模拟 Pulsar/BookKeeper 的“写入多个 Bookie、达到 ack quorum 才确认”的可靠性机制,帮助理解消息不丢的原理。
  1. chosen = random.sample(self.bookies, self.write_quorum)随机选择 write quorum 个 Bookie 节点,模拟多副本写入。
  2. bookie.append(self.entry_id, data)把消息条目写入一个 Bookie,相当于仓库里的一个货架完成上架。
  3. if acks >= self.ack_quorum:只有确认数量达到阈值,才向生产者返回成功。
  4. raise RuntimeError("ack quorum not reached")确认数不足时直接报错,确保不会给生产者虚假成功。
  5. broker.read(entry_id)从任一存活 Bookie 读回数据,模拟故障后仍可恢复消息。

常见误区

误区看到 Pulsar 也用 topic、producer、consumer,就以为它只是 Kafka 换了个 Logo,选型时直接当 Kafka 用。

正解Pulsar 把计算和存储拆成两层,Broker 无状态;Kafka 的数据副本和 broker 强绑定。Pulsar 扩容 broker 不用搬数据,存储扩容也不依赖分区迁移,这是架构层面的本质区别。

为何常见:第一眼看到相同 API 容易忽略底层,而且 Kafka 先入为主,容易把“消息队列”直接等同于 Kafka。

误区只要用共享订阅,所有消息顺序都会打乱,因此有人宁可牺牲吞吐也不用 Pulsar。

正解Shared 确实只保证单个消费者内有序;但 Key_Shared 模式按 key 路由,同一个 key 总是发给同一个消费者,既大幅提升吞吐又保证同 key 顺序。顺序和吞吐在 key 维度上可以兼得。

为何常见:拿 Kafka 分区顺序的思维套 Pulsar,没理解 Key_Shared 的按 key 粘性路由。

误区消息上了对象存储就等于进冷宫,查询要先把数据下到本地集群,耗时又费钱。

正解Pulsar 分层存储只是把 segment 卸载到 S3/HDFS,读取时 Broker 和 BookKeeper 会流式读对象存储,不需要先整体导回。消费者的订阅游标可以平滑穿越冷热数据。

为何常见:过去见过太多“冷备份=回滚”的系统,容易默认认为冷数据必须先全量恢复才能使用。

误区为了不丢消息,把 ack quorum 配成所有副本数,认为这样最保险。

正解ack quorum 越大,写入延迟越高;可靠性来自“多数确认后仍留有冗余”。例如 3 副本时,ack=2 已可容忍 1 个节点故障,兼顾延迟和可用性。设成 3 会牺牲性能,还降低可用性——一个节点故障就可能写不进去。

为何常见:只看到“多写几个更稳”,没意识到确认阈值与可用性之间的权衡。

面试题

Q1. Pulsar 中真正负责消息持久化存储的组件是?

单选题 · easy

  • A.Broker 节点
  • B.BookKeeper 集群← 正确答案
  • C.ZooKeeper 集群
  • D.Producer 端缓存

答案:B。Broker 只做路由和派单,像分拣台;真正把包裹放进货架的是 BookKeeper 集群。ZooKeeper 负责元数据协调,Producer 只管发消息。

Q2. Pulsar 多租户资源隔离的正确层级顺序是?

单选题 · easy

  • A.租户、命名空间、主题← 正确答案
  • B.主题、租户、命名空间
  • C.命名空间、租户、主题
  • D.命名空间、主题、租户

答案:A。Pulsar 的层级是 tenant 包含 namespace,namespace 包含 topic。就像物流园先分快递公司,再分库区,再分货道。

Q3. 既要求同一个 Key 的消息有序,又要提升消费吞吐,应该选择哪种订阅模式?

单选题 · mid

  • A.独占订阅模式
  • B.共享订阅模式
  • C.键共享订阅模式← 正确答案
  • D.故障转移订阅模式

答案:C。Key_Shared 按 key 把消息路由到固定消费者,同一 key 有序,不同 key 并行,兼顾吞吐和顺序。独占只有一个消费者,共享不保证同 key 顺序。

Q4. 三个 Bookie 组成存储集群,配置 write_quorum=3、ack_quorum=2,最多容忍几个节点同时故障而不丢失消息?

单选题 · mid

  • A.1 个节点← 正确答案
  • B.2 个节点
  • C.3 个节点
  • D.0 个节点

答案:A。ack quorum 为 2 时,消息已经由 2 个副本确认写入,允许这 2 个中最多 1 个故障,另一个仍能保住数据,所以 f_max = 2 - 1 = 1。

Q5. Pulsar 分层存储卸载旧数据的主要目标是什么?

单选题 · mid

  • A.降低 Broker 内存压力
  • B.降低 BookKeeper 磁盘成本← 正确答案
  • C.提高消息发送速度
  • D.增加消息副本数量

答案:B。分层存储把冷 segment 放到 S3/HDFS,BookKeeper 只保留热数据,从而大幅降低存储成本。它主要解决容量成本问题,而不是发送速度。

Q6. Pulsar 跨地域复制通常采用哪种一致性模型?

单选题 · hard

  • A.强一致性
  • B.线性一致性
  • C.最终一致性← 正确答案
  • D.会话一致性

答案:C。跨地域复制是异步复制,北京写入后上海不会立刻可见,需要等待复制完成,因此属于最终一致性。这也是大多数跨地域消息系统的默认选择。

Q7. Pulsar Broker 可以秒级扩容的最核心原因是?

单选题 · mid

  • A.使用 SSD 盘存储数据
  • B.数据全部放在 ZooKeeper
  • C.Broker 无状态且数据在 BookKeeper← 正确答案
  • D.采用内存消息队列

答案:C。Broker 不持有消息数据,新节点加入后只需认领 topic 路由任务,不需要复制存量数据。分拣台是空的,随时可以加。

Q8. 以下哪些属于 Pulsar 的订阅模式?

多选题 · easy

  • A.独占订阅← 正确答案
  • B.共享订阅← 正确答案
  • C.广播订阅
  • D.键共享订阅← 正确答案

答案:ABD。Pulsar 支持 Exclusive、Shared、Failover、Key_Shared,没有标准的“广播订阅”。独占单消费者、共享多消费者、KeyShared 按 key 保证有序。

Q9. 下列对 Pulsar 特点的描述,正确的有哪些?

多选题 · hard

  • A.支持多租户隔离← 正确答案
  • B.不支持持久化存储
  • C.支持分层存储← 正确答案
  • D.支持跨地域复制← 正确答案

答案:ACD。Pulsar 默认由 BookKeeper 持久化消息,不是内存队列;同时也支持多租户、分层存储和跨地域复制。B 明显错误。

Q10. BookKeeper 保证消息可靠性的常用机制包括哪些?

多选题 · mid

  • A.多副本冗余写入← 正确答案
  • B.达到确认数才响应← 正确答案
  • C.消灭全部磁盘故障
  • D.日志顺序落盘保存← 正确答案

答案:ABD。BookKeeper 把消息写入多个 Bookie,journal 顺序落盘,并基于 ack quorum 确认。它无法消灭磁盘故障,只是通过冗余让少数故障不影响数据安全。