快递行业有一条铁律:分拣台再快,库房不够也会爆仓;库房再大,分拣台人手不足也会积压。Apache Pulsar 就像一个把分拣台和立体仓库彻底分开的现代物流园——分拣台(Broker)只负责接单和派单,包裹真正的存放全交给独立仓库(BookKeeper)。Kafka 则是老式仓配一体楼,货架、分拣台挤在同一个空间,扩容计算往往要搬走数据。Pulsar 靠这种架构,天然获得秒级扩容、多租户、多订阅模式、分层存储、跨地域复制等能力,成为流处理消息队列的新宠。
学会本讲,你将理解 Pulsar 为什么比 Kafka 更适合云原生场景,并能在日志管道、实时特征、事件驱动架构中做出有说服力的技术选型。
可视化演示
展示Pulsar核心优势:计算存储分离架构、四种订阅模式、与Kafka对比选型。切换模式查看不同维度。
本节配有交互动效(进入视口后自动加载,约 3KB)。
架构革新
快递物流园里,决定每天能处理多少包裹的,是分拣台和仓库两个环节的短板。传统 Kafka 像一座“仓配一体”老楼——货架、分拣台挤在同一层,想多加一台分拣机,得先搬走楼里的货。Pulsar 则把物流园拆成两个独立系统:分拣台(Broker)只负责接单和派单,立体仓库(BookKeeper)只负责存包裹。这种“计算存储分离”的架构,是 Pulsar 一切优势的地基。
在 Pulsar 中,Broker 是无状态服务,不保存任何消息数据;消息以 segment 形式写入 BookKeeper 的 ledger 中。消费者要从哪个 topic 读,Broker 临时去仓库取,读完数据仍留在仓库。Kafka 的每个 broker 必须带着分区副本,所以扩缩容总伴随数据迁移。
这样一来,你不用担心仓库放不下还要给所有分拣台搬家;哪边缺,扩哪边,这就是 Pulsar 架构革新的价值。
- B×t_bBroker 数量乘以单个 Broker 吞吐,代表分拣台的总处理能力。
- K×t_sBookKeeper 节点数乘以单节点存储吞吐,代表仓库的吞吐上限。
- min(·)整个系统最终吞吐等于两个上限中较小的值,瓶颈在哪边就先扩哪边。
弹性扩容
双十一快到了,分拣台不够用,你只需要在空地上搭几张新分拣台。在 Pulsar 中,新 Broker 启动后从元数据服务认领 topic,不需要复制任何存量数据,所以加机器以秒计。Kafka 加 broker 时需要重新分配 partition 和副本,数据搬迁往往以小时计。
Pulsar 的存储层缩容同样灵活。因为数据在 BookKeeper 里,Bookie 节点可以逐步下线并让 ledger 自动重新分布。计算层与存储层互相不绑架,弹性伸缩自然更快。
物流园的双十一不再可怕:先加分拣台顶住峰值,再去扩仓库容量,两边互不拖累。
- ΔB新增的 Broker 数量,相当于多出来的分拣台。
- t_b单个 Broker 的吞吐,相当于每张分拣台的处理速度。
- if 条件只有当存储层余量充足时,新增分拣台才能真正提升整体吞吐。
多租户隔离
大型物流园不会让所有快递公司共用同一个库区,而是给每家租户划分独立库区,货架、道闸、月台分开管理。Pulsar 的多租户模型也这样:tenant 是快递公司,namespace 是库区,topic 是货道。管理员可以为每个 namespace 设置存储配额、消息速率和权限。
一台 Pulsar 集群可以同时服务多个业务团队,每个团队只看得见自己的租户。某个团队流量暴涨时,最多触到自己的配额上限,不会挤爆邻居的库区。Kafka 要实现类似隔离,通常需要为每个团队单独铺集群,运维成本高出一大截。
库区门禁管好了,几十个团队共用一套物流园也不会互相串货。这就是多租户隔离的价值。
- used_i第 i 个租户当前已使用的资源量,比如存储、带宽或消息速率。
- quota_i管理员给第 i 个租户设定的配额上限。
- ≤1使用率不超过 1 时一切正常,超过 1 就触发限流。
订阅模式
同一个 topic 里的消息,可以像包裹一样有不同的取件方式。独占订阅是只请一个人签收;故障转移是正选签收人不在时备选人顶上;共享订阅是十个人一起拆包裹,谁有空谁拿;KeyShared 是按收货地址分人,同一个地址永远由同一个人负责,保证同一 key 的消息不乱序。
多订阅模式让 Pulsar 比 Kafka 灵活得多。Kafka 的消费组本质上更像故障转移加共享的混合体,但无法在同一份数据上按需切换。Pulsar 在消费端可以随时创建不同订阅,互不影响。
取件方式可以按需换:要严格单播就独占,要高吞吐就共享,要保序又高吞吐就 KeyShared,一个系统全部搞定。
- r_i第 i 个消费者在共享订阅中的消费速率。
- 求和共享订阅总吞吐等于所有消费者速率之和,人数越多,吞吐越高。
- key_sharedKeyShared 按 key 分配,不同 key 可以由不同消费者并行处理。
数据可靠性
仓库收货时不是把包裹随手一放就告诉商家“已签收”,而是同时放进好几个货架,至少有两个货架回复“放好了”,商家才收到成功回执。BookKeeper 的 ack quorum 就是这个“至少两个”的签收阈值。
Pulsar 的每个消息条目会写入 write quorum 个 Bookie 节点,拿到 ack quorum 个节点的确认后才告诉生产者成功。只要故障节点数不超过 ack quorum 减一,至少还有一个已确认的副本存活,消息就不会丢。
确认签收机制让物流园不必等所有货架都回话才告诉商家,既保证不丢货,又保住响应速度。
- Q_aack quorum,即至少需要多少个副本回复“已写入”才算成功。
- Q_a-1因为已经确认的副本有 Qa 份,所以允许其中有 1 份丢失,仍至少有 1 份安全。
- f_max故障容忍上限,超过这个数就可能丢数据。
分层存储
仓库容量有限,把几个月前的包裹打包送去地下档案室,取件时再按编号流式调取。Pulsar 的分层存储把老 segment 卸载到 S3/HDFS,BookKeeper 只保留热数据,topic 的保留时间从“几周”变成“长期甚至无限”。
分层存储对 AI 场景尤其有用。训练样本需要回溯 90 天甚至更久,把 90 天前的数据全放 BookKeeper 成本太高。Pulsar 自动把冷 segment 上传对象存储,消费者依然能从同一个订阅里读历史消息,不需要重新搭一套离线管道。
档案室解决了仓库爆仓问题,历史消息不再需要忍痛删除,回溯窗口由成本决定,而不是由架构决定。
- S_hotBookKeeper 热存储层能容纳的数据量。
- S_cold对象存储冷层能容纳的数据量,通常近乎无限。
- v_write每天新写入的数据速度。
跨域复制
一个物流园不够,在全国建两个园,面单数据异步同步。Pulsar 的跨地域复制也是异步的:北京写一条消息,后台异步复制到上海。消费者在两个地域都能读到副本,实现多活和容灾。
跨地域复制让 AI 平台可以“就近读写”。例如北京和上海各部署推理服务,日志各自写入本地 Pulsar,再互相异步复制;某个地域故障时,另一个地域的消费者还能继续消费相同的数据。
双物流园互相同步,任何一边台风停电,另一边都能继续运转,这就是跨地域复制的底气。
- RPO灾难恢复目标,代表最多允许丢失多长时间的数据。
- Δ_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 演示
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()
chosen = random.sample(self.bookies, self.write_quorum)随机选择 write quorum 个 Bookie 节点,模拟多副本写入。bookie.append(self.entry_id, data)把消息条目写入一个 Bookie,相当于仓库里的一个货架完成上架。if acks >= self.ack_quorum:只有确认数量达到阈值,才向生产者返回成功。raise RuntimeError("ack quorum not reached")确认数不足时直接报错,确保不会给生产者虚假成功。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 中真正负责消息持久化存储的组件是?
- A.Broker 节点
- B.BookKeeper 集群← 正确答案
- C.ZooKeeper 集群
- D.Producer 端缓存
答案:B。Broker 只做路由和派单,像分拣台;真正把包裹放进货架的是 BookKeeper 集群。ZooKeeper 负责元数据协调,Producer 只管发消息。
Q2. Pulsar 多租户资源隔离的正确层级顺序是?
- A.租户、命名空间、主题← 正确答案
- B.主题、租户、命名空间
- C.命名空间、租户、主题
- D.命名空间、主题、租户
答案:A。Pulsar 的层级是 tenant 包含 namespace,namespace 包含 topic。就像物流园先分快递公司,再分库区,再分货道。
Q3. 既要求同一个 Key 的消息有序,又要提升消费吞吐,应该选择哪种订阅模式?
- A.独占订阅模式
- B.共享订阅模式
- C.键共享订阅模式← 正确答案
- D.故障转移订阅模式
答案:C。Key_Shared 按 key 把消息路由到固定消费者,同一 key 有序,不同 key 并行,兼顾吞吐和顺序。独占只有一个消费者,共享不保证同 key 顺序。
Q4. 三个 Bookie 组成存储集群,配置 write_quorum=3、ack_quorum=2,最多容忍几个节点同时故障而不丢失消息?
- A.1 个节点← 正确答案
- B.2 个节点
- C.3 个节点
- D.0 个节点
答案:A。ack quorum 为 2 时,消息已经由 2 个副本确认写入,允许这 2 个中最多 1 个故障,另一个仍能保住数据,所以 f_max = 2 - 1 = 1。
Q5. Pulsar 分层存储卸载旧数据的主要目标是什么?
- A.降低 Broker 内存压力
- B.降低 BookKeeper 磁盘成本← 正确答案
- C.提高消息发送速度
- D.增加消息副本数量
答案:B。分层存储把冷 segment 放到 S3/HDFS,BookKeeper 只保留热数据,从而大幅降低存储成本。它主要解决容量成本问题,而不是发送速度。
Q6. Pulsar 跨地域复制通常采用哪种一致性模型?
- A.强一致性
- B.线性一致性
- C.最终一致性← 正确答案
- D.会话一致性
答案:C。跨地域复制是异步复制,北京写入后上海不会立刻可见,需要等待复制完成,因此属于最终一致性。这也是大多数跨地域消息系统的默认选择。
Q7. Pulsar Broker 可以秒级扩容的最核心原因是?
- A.使用 SSD 盘存储数据
- B.数据全部放在 ZooKeeper
- C.Broker 无状态且数据在 BookKeeper← 正确答案
- D.采用内存消息队列
答案:C。Broker 不持有消息数据,新节点加入后只需认领 topic 路由任务,不需要复制存量数据。分拣台是空的,随时可以加。
Q8. 以下哪些属于 Pulsar 的订阅模式?
- A.独占订阅← 正确答案
- B.共享订阅← 正确答案
- C.广播订阅
- D.键共享订阅← 正确答案
答案:ABD。Pulsar 支持 Exclusive、Shared、Failover、Key_Shared,没有标准的“广播订阅”。独占单消费者、共享多消费者、KeyShared 按 key 保证有序。
Q9. 下列对 Pulsar 特点的描述,正确的有哪些?
- A.支持多租户隔离← 正确答案
- B.不支持持久化存储
- C.支持分层存储← 正确答案
- D.支持跨地域复制← 正确答案
答案:ACD。Pulsar 默认由 BookKeeper 持久化消息,不是内存队列;同时也支持多租户、分层存储和跨地域复制。B 明显错误。
Q10. BookKeeper 保证消息可靠性的常用机制包括哪些?
- A.多副本冗余写入← 正确答案
- B.达到确认数才响应← 正确答案
- C.消灭全部磁盘故障
- D.日志顺序落盘保存← 正确答案
答案:ABD。BookKeeper 把消息写入多个 Bookie,journal 顺序落盘,并基于 ack quorum 确认。它无法消灭磁盘故障,只是通过冗余让少数故障不影响数据安全。