我最早关注Pulsar,是在一次公司内部的技术选型讨论上。当时团队正在为一个实时数据中台项目挑选消息中间件,Kafka、RabbitMQ来回过了好几轮,突然有同事抛出一句:要不看看Pulsar?说实话,那时候我连这个名字都念不顺溜,更没想过它会在后来的大促链路里成为扛住千万级消息洪峰的关键角色。后来花了两个周末把官方文档啃完,又在小规模集群上跑了压测,我才意识到Pulsar身上那种"云原生消息队列"的底气不是营销话术——它把存储和计算彻底拆开,用Apache BookKeeper做底层存储,Broker只负责消息路由和负载管理,这种架构设计让很多困扰Kafka多年的老问题直接消失。如果你正面临消息队列选型,或者被多租户、跨地域复制、存储成本这些需求反复折磨,这篇"初识"应该能帮你建立对Pulsar的核心认知。
1. 从Kafka的痛到Pulsar的香:选型背后的思考
1.1 传统消息队列在云原生时代的尴尬
先聊一个最直观的痛点:Kafka的存储和计算是绑在一起的。每个Broker节点既是计算单元又是存储单元,分区一旦创建,它的Leader和副本就固定在某些节点上。写入流量大了,你想扩容,新节点加入后需要做分区重平衡,这个过程中副本迁移会占用大量网络和磁盘I/O,搞不好还会影响在线读写。当时我们有个大Topic单日写入量在上亿条,分区数已经堆到上百个,每次扩容都是一场心惊肉跳的运维大考。
RabbitMQ的情况又不太一样。它擅长做复杂路由和灵活的消息模式,但吞吐量到了千万级就有点吃力,消息堆积时内存和磁盘的表现也不够理想。更麻烦的是,它默认把消息存在内存或本地磁盘,节点挂了恢复起来非常费劲。你可能会说,用镜像队列不就完了?但多节点同步带来的网络开销和性能损耗,在高峰期真的会让人怀疑人生。
这些传统中间件在物理机时代够用,但放到云原生环境里就尴尬了。容器调度要求组件能随时随地弹性伸缩,存储要么挂云盘要么交给独立存储服务,而Kafka这种"计算存储一体"的模型,Pod漂移、节点故障都会牵一发动全身。Pulsar之所以能进入视野,就是因为它从底层架构上改变了游戏规则:计算节点不存数据,数据节点不碰计算。
1.2 Pulsar到底是个什么来头
Pulsar是Apache软件基金会的顶级项目,由Yahoo在2013年左右孵化、2018年捐赠给Apache,现在已经是消息队列领域不可忽视的一股力量。它的核心卖点可以浓缩成几句话:采用存算分离的分层架构,Broker是无状态的,消息数据交给BookKeeper集群持久化;原生支持多租户,用Property/Namespace做隔离;支持跨地域复制,可以在多个数据中心之间同步消息;同时兼容Kafka API,很多客户端不用改代码就能切过来。
一开始我对这些特性保持怀疑,毕竟"什么都能干"的东西往往什么都干不好。但等我真正搭了一套三节点的Pulsar集群跑业务压测,才明白它的架构优势确实能兑现。最直观的感受是扩容:加Broker只需要把新的无状态节点接进来,不用搬任何数据;加存储节点也只需要等BookKeeper自动做数据重分布。整个过程中,消费者几乎感知不到抖动。这种体验在Kafka上我是从未有过的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Pulsar核心架构拆解:为什么它敢叫云原生
2.1 三层架构:Broker、BookKeeper、元数据服务
Pulsar的部署架构可以粗略看成三层。最上层是Broker,它相当于一个轻量的路由器,负责接收生产者的消息、把消息写入存储、再推送给消费者,同时管理各种订阅状态。Broker本身不保存消息数据,所以它是无状态的,可以随意增删和滚动升级。
中间层是BookKeeper集群,每个节点叫Bookie。这里才是消息数据的真正归宿。Broker把消息按Segment为单位写入Bookie,Bookie负责数据的持久化、复制和恢复。BookKeeper本身就是Apache旗下的分布式存储系统,专为日志类数据设计,具有低延迟、高可用、强一致的特性。
最底层是元数据服务,Pulsar用ZooKeeper(在较新版本中也可用etcd)管理整个集群的元数据,比如Broker的注册信息、Topic的分布、租户权限配置等。不过ZooKeeper只存元数据,不存消息,压力不大。
这三层各司其职的架构,让Pulsar在云原生环境里如鱼得水。Broker可以做成无状态Pod,配合Kubernetes的HPA按流量自动扩缩容;Bookie需要稳定存储,就挂上云盘的StatefulSet;ZooKeeper当标准有状态服务管理。哪一层出问题就单独扩容哪一层,完全不互相牵扯。
2.2 Segment机制:一份消息数据如何被优雅地管理
BookKeeper存储消息的方式很巧妙,它把每个Topic的消息流切分成一个个Segment,每个Segment内部又包含多个Ledger片段。写入时,多个Bookie组成一个Ensemble,消息被同时写入多个Bookie形成副本,默认是2副本,也就是同一份数据写两份,保证一个节点挂了数据不丢。
这个设计跟Kafka的Partition有本质区别。Kafka里一个Partition是一个连续的日志文件,只能由Leader节点接收读写;而Pulsar的Topic底层是一个Segment列表,每个Segment可以分布在不同的Bookie组合上,不同Segment甚至可以走不同的副本策略。也就是说,一个Topic的写入压力天然就被分散到了多台存储节点上,不存在单点瓶颈。
Segment还有一个特别实用的特性:可去重。Kafka的Partition文件只能顺序追加,想清理旧数据得做日志压缩或者整段删除;Pulsar则可以把不再被任何订阅消费的Segment直接剥离释放。配合可配置的留存策略(可以按时间、按大小、按消息条数设置TTL),清理存储再也不用像Kafka那样手动触发什么cleanup脚本。我印象很深的是一次压
