不知道大家有没有这种感觉:这几年聊到消息中间件,再也不像前几年那样只有Kafka一个声音,团队里做技术选型时讨论得越来越细。是选Kafka保持“主流”,还是把目光转向Apache Pulsar,用存算分离的思路来应对更复杂的业务压力?这个问题我反反复复被问到过,也是每次开源活动上互动最热烈的环节。刚好COSCon‘25的同场活动Pulsar Developer Day议程放了出来,作为一个从0到1摸过Pulsar、也在生产环境里踩过不少坑的人,我觉得有必要结合这个消息中间件领域的动态,把Pulsar到底适合谁、能解决什么问题、实际跑起来有哪些门道,系统性拆一遍。无论你是正在做技术选型,还是已经在用Pulsar但想找更多实践参考,这篇内容应该都能帮上忙。
这次Pulsar Developer Day最大的价值不在于告诉你Pulsar“很牛”,而是把消息中间件的底层原理、真实业务场景、以及社区沉淀的运维经验集中摆出来。我始终认为,看技术会议不能只看热闹,关键是想清楚一件事:它解决了我的什么问题,别人踩过的坑我要怎么绕过。所以下面我不去逐条复述议程表,而是从我的实际经验出发,把Pulsar值得关注的特性、部署调优时的关键参数、以及日常运维最容易翻车的几个隐蔽问题,掰开揉碎讲清楚。
1. 消息中间件选型进入深水区,Pulsar为什么值得被认真对待
1.1 从Kafka到Pulsar,大家在纠结什么
先说一个我自己亲身经历的场景。之前在一家日活千万级的平台做技术架构升级,消息链路承担了订单事件、用户行为日志、风控实时计算、数据同步四类核心流量。早期用的Kafka集群规模一路增长,整个团队最怕的就是两件事:一是Broker节点磁盘故障导致分区不可用,二是扩容时Rebalance引发的长时间分区不可用。这些痛点是Kafka架构本身带来的,它把分区的存储和服务的状态绑在一起,数据落在本地磁盘,想扩展存储能力或者做平滑的节点升级,操作成本和风险都不小。很多团队在Kafka集群上百个节点之后都会遇到类似瓶颈,这也是Pulsar开始被频繁提起的原因。
Pulsar最核心的差异在于它把“计算”和“存储”分开设计和独立扩展。Broker不保存数据,只负责消息的路由、订阅管理、各项协议处理,真正干存储的是一套叫Apache BookKeeper的底层系统。这么做带来的直接好处,就是任何Broker节点都可以随时增减,因为底层数据并不依赖这些节点存在。我在测试环境里验证过,干掉一个Broker节点,只要元数据服务还在,生产消费几乎不受影响,数据也不会丢。这种架构基因上的不同,决定了它在弹性伸缩、跨地域复制、多租户隔离这些场景下的表现,和Kafka走的是完全不同的路子。
但我不建议团队仅仅因为“Pulsar架构听起来更先进”就盲目切换。消息中间件选型本质上是对团队运维能力、业务模型和长期规划的综合考量。Kafka的优势在于生态成熟、人力资源好找、周边组件齐全;Pulsar的优势在于更现代的架构设计,尤其当你对“存储成本”和“数据弹性”有较高要求时。这里可以把Kafka理解为一家生意火爆但场地固定的实体店,客人多起来就只能不断在店里加椅子;Pulsar更像是仓库和门店分离的连锁模式,门店不够就开新店,仓库压力大就单独扩仓库,互不拖累。
1.2 Pulsar到底拿什么解决实际问题
围绕着Pulsar的技术讨论虽然多,真正落到实际业务上,它出众的优势集中在几个具体方向。第一是多租户能力,你可以用租户和命名空间把不同部门、不同环境、不同优先级的数据彻底隔离开。以前在公司里,订单组和数据组共享一套Kafka,互相影响的时候协调成本极高。改成Pulsar之后,用不同的租户隔离资源和配额,单租户突增流量不会影响别人,权限也好控制。第二个是存储成本,Pulsar支持分层存储,你可以把旧数据卸载到对象存储,比如AWS S3、阿里云OSS或者自建MinIO。这个能力对于一个需要保留大量历史消息做离线分析的平台来说,节省的成本不是小数目。我们在生产环境里把超过三天的消息自动卸载到对象存储,本地BookKeeper只保留热数据,集群负载降下来一个量级。
还有一点容易被忽略的是Pulsar的多协议支持,它原生支持Kafka协议。这一点在系统演进里价值很大,意味着你不需要重写现有客户端就可以逐步迁移。我看过不少团队采用双跑策略,生产上同时保留Kafka和Pulsar,一部分流量慢慢往里切,切换过程中客户端代码完全复用,大大降低了迁移门槛。这也是我觉得Pulsar特别适合存量业务的原因:它给你的不是一次釜底抽薪的重写,而是一条平滑迁移的路径。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发者在Pulsar里最该吃透的几个核心原理
2.1 存算分离到底解放了谁
Pulsar的架构里有两个角色必须分清,一个是Broker,负责处理请求;另一个是BookKeeper的Bookie节点,负责数据落盘和复制。把这两者剥离开之后,消息中间件这个系统突然变得灵活了很多。对于在线流量波动剧烈的场景,你只需要快速扩充Broker,把这些无状态节点摊开承接连接和读写请求;对于数据量大规模增长、磁盘吃紧的状况,扩充Bookie节点调整存储容量就行。它们在物理上独立,扩容时可以各扩各的,比经典的消息队列架构有着本质的不同。
我还记得第一次被拉去处理Pulsar集群线上问题时,定位的思路也为此发生了改变。以前排查消息队列故障,基本上是在一堆节点里找到那个“状态异常”的Broker,再把它的分区迁移走。到了Pulsar这里,会先分两大类:是Broker的问题还是Bookie的问题?如果是订阅游标、连接数、负载均衡层面的原因,优先查Broker的状态和参数;如果是写入延迟、存储可用性相关的问题,重点去查BookKeeper集群。这种责任边界清晰的设计,在跨团队协作时也很有优势,存储团队和消息团队可以互不干扰,各自优化自己负责的层。
2.2 订阅模型和消费语义不搞清楚,后面是要还账的
Pulsar的消费模型比传统消息队列多了一些花样,不能想当然套以前的习惯。它提供了四种订阅类型:独占订阅、共享订阅、故障转移订阅、按键共享订阅。流量小、要求严格顺序的场景,通常用独占订阅或者故障转移订阅;吞吐量大、消费端任务可以并行的场景,经常用共享订阅,让消息轮询分发到多个消费者。按键共享订阅解决的则是同一个业务key的消息必须被同一个消费者处理的问题,适合订单状态机一类的业务,不用牺牲并行度同时又能保证key级别的顺序。
顺序语义可以说是在所有消息中间件里最容易被误解的部分。Kafka保证的是分区内的顺序,Pulsar同样如此。但在共享订阅模式下,如果业务方不加区分,消息很可能被多个消费者并行处理,打乱了原有的先后顺序。我在设计订单事件流时踩过这个坑,最开始时为了吞吐量用了共享订阅,结果订单状态流转偶发异常,排查很久才发现源头就是消息顺序乱了。后来改成按键共享订阅,以订单号作为key,同一订单消息全部进入同一个消费者,吞吐量并没有下降太多,因为不同订单依然可以分配到不同消费者上,顺序和性能算是兼得了。类似这种消费模型的选择问题,应该是开发者接触Pulsar时最先要建立的意识。
3. 从活动内容反推Pulsar落地路线图
3.1 这类技术活动通常讲什么,以及为什么值得听
虽然这篇不是议程复述稿,但你可以通过内容的组织方式,反推出Pulsar从入门到深入的一条完整学习路径。论坛风格的议程一般会把重心放在几个层面:顶层的架构设计是怎么演进的,底层存储引擎BookKeeper有哪些优化空间,消息中间件在具体行业场景里的落地方式,以及开源社区的版本规划与生态发展。这些模块对不同类型的开发者都有针对性:架构师关心设计取舍和选型依据;一线开发关心客户端使用和API设计;运维同学则更关注部署形态和监控告警、故障演练。一个活动能把这些角色都覆盖到,说明Pulsar的生态已经相对立体了,不是停留在概念阶段。
我自己的经验是,参会不要只听那些“高屋建瓴”的分享,更多要关注里面讲问题排查和故障处理的内容,尤其是生产环境的真实案例复盘。Pulsar官方文档写得已经很完整,但很多只能在生产环境里遇到的细节,比如特定版本升级会遇到的元数据不兼容、扩容时Bookie与Broker配比失衡引发的性能问题、某个版本客户端连接数过高对Broker造成的压力,这些东西在文档里几乎找不到,只能靠有实际经验的人反复趟出来。活动里只要有一个成熟用户愿意讲失败经历,这一场就算没白去。
3.2 把议程当作一份学习地图
如果没有办法到现场,拿活动议程当自学路线的框架也是完全可行的。把议题内容对应成一个个技术专题,顺着这些关键词去查文档、做实验,效果比漫无目的翻阅源码好得多。我的建议是先搭一套最小集群,亲手体验一下Pulsar的部署和管理,在本地环境中把消息发起来、消费掉,再逐步增加复杂度,模拟多租户隔离、消息积压、Broker故障这样一些常见的场景。对大多数开发者来说,动手跑一遍比看十个分享都管用。
一种快速上手的路径是使用Docker启动Pulsar独立模式,一条命令就能在本地把Broker和Bookie跑起来。生产环境目前最常见的部署方式是Kubernetes,官方提供了完整的Helm Chart,把Pulsar集群拆分成各个组件分别管理。在Kubernetes上跑Pulsar会有一个好处是,Broker和Bookie分别作为不同的工作负载,利于弹性伸缩。操作步骤大致可以总结为:先准备好Kubernetes环境和Helm;然后添加Pulsar官方Chart仓库,更新本地索引;按实际需要覆盖values.yaml中的配置项,诸如副本数、存储类、资源配额;最后执行helm install命令把集群部署起来。如果只是想跑通一个验证环境,用kind搭建一个单节点Kubernetes集群就够了。
4. 生产级Pulsar集群部署与调优的实操建议
4.1 常见部署形态的参数选择
在实际部署Pulsar集群时,大部分团队会采用以下三种形态:最小的开发环境用Docker独立模式;测试和预发环境建议至少起3个Bookie、2个Broker和3个元数据服务节点,以模拟生产环境的容错能力;大规模生产环境推荐采用Kubernetes部署,并把元数据服务、Bookie、Broker分别拆分管理。这边有一个非常实际的坑要提醒,很多团队在刚开始搭Pulsar集群时,只关注Broker的规格,把Bookie节点当成普通存储节点来配。结果流量一上来,瓶颈全部集中在Bookie的磁盘IO上,整个集群的读写延迟立即飙升。
BookKeeper的写入性能和磁盘选型直接挂钩。日常使用中,每台Bookie机器至少要配备一块独立的高性能磁盘作为Journal盘,专门承载写入日志。Ledger数据可以放在另一块容量更大的磁盘上,两类磁盘的IO隔离做好了,Pulsar的写入稳定性才会有基本保障。在机械硬盘还是SSD的选择上,我的建议是量力而行。对于生产环境,SSD带来的延迟下降非常明显,尤其是在Journal盘上,一块普通的SATA SSD都比机械硬盘好很多。对于只追求极致性能的场景,NVMe SSD几乎是必须的。这里我用表格对比一下常见的部署配置。
| 部署环境 | Broker数量 | Bookie数量 | 元数据节点数量 | 推荐机器规格 | 适用场景 |
|---|---|---|---|---|---|
| 本地开发 | 1 | 1 | 1 | 4C8G Docker | 功能验证与学习 |
| 测试环境 | 2 | 3 | 3 | 8C16G SSD | 联调和压测 |
| 生产小规模 | 3 | 5 | 3 | 16C32G NVMe | 日消息量亿级以内 |
| 生产大规模 | 按需水平扩展 | 按存储量扩展 | 3或5 | 32C64G+ NVMe | 日消息量亿级以上 |
我见过不少人一开始在测试环境图省事,把Bookie和Broker混部在几台机器上。这种做法在低流量下看不出问题,一旦进入压测阶段,磁盘竞争和CPU竞争立刻会让整个集群变得极不稳定。所以只要条件允许,生产环境请把Broker和Bookie分开部署。这个道理就像餐厅的前厅和后厨不能共用一条通道一样,前厅要快,后厨更要稳,混在一起只会互相干扰。
4.2 客户端连接、内存与租户隔离的常见配置
Pulsar的各种参数数量不少,老实说没有一套放之四海而皆准的配置,但有几个方向的参数值得格外留意。首先是Broker的内存设置,Pulsar的Broker默认会使用系统可用内存的一部分作为缓存,这会直接影响消息读写的缓存命中率。建议结合机器规格设置JVM堆内存,同时仔细规划直接内存的大小。JVM堆内存设置得太小会导致GC频繁,设置得太大又可能会剥夺系统直接内存的缓存空间,实际压测下来,总内存中预留足够比例给直接内存,往往对缓存命中率的提升更明显。
和很多系统一样,连接数也是Pulsar最容易出现的瓶颈。客户端的连接不要无限地创建,尽量复用连接。如果业务方以较高频率启动并销毁客户端实例,比如在函数计算这类动态扩缩容的环境中运行Pulsar客户端,很容易把Broker的连接数冲上去,造成整体负载偏高。通过配置客户端的连接池机制来复用底层连接,以及在使用完生产者或消费者之后主动关闭,是两条非常基础但有效的操作。
还有一个很容易被忽略但极其重要的点是租户和命名空间的资源隔离配置。如果这个集群是多个团队共享的,我强烈建议为每个核心业务单独规划租户,在租户或命名空间级别设置消息积压配额和存储配额。没有做配额管理时,一个团队的消息堆积可能导致存储空间被打满,进而拖垮整个集群上所有业务,这种事故最好一次都不要经历。
4.3 消息积压和性能抖动怎么排查与恢复
消息积压是消息中间件运维中最高频出现的问题。不管本身架构多好,一旦某个消费者服务出问题或下游处理能力下降,积压就会出现。Pulsar提供了比较灵活的消息回放能力,可以重新消费积压的消息。这个能力基于它保留了完整的消息留痕,不会因为消息被消费过就直接删除。在实际恢复过程中,我一般的处理顺序是:先定位是生产端堆积还是消费端堆积,再检查消费者的处理耗时和失败重试次数。在消费端处理能力不足时,优先考虑增加共享订阅的消费者数量;如果是单条消息处理逻辑本身太慢,重点优化代码或把消费者对下游的调用改成批量方式。很常见的是下游数据库或接口慢,拖着消息消费速度起不来。这个时候盲目扩容消费者是没用的,反而会加重下游的压力。
性能抖动问题大家遇到最多的表现:某个时间点开始,Pulsar的写入延迟和读取延迟突然升高,过一段时间又自己恢复了。这类问题常常和以下原因绑定:某个磁盘IO占满、某个Bookie节点被大数据量的Ledger写入拖累、某个租户的突发流量。排查这类问题的建议是先把Pulsar的监控指标完整地建立起来,至少要关注Broker的负载、Bookie的写入延迟、磁盘利用率、JVM GC耗时等关键指标。有了指标之后才能把抖动点定位到具体的组件,不然只能瞎猜。
5. Pulsar社区与生产落地的距离
5.1 开源活动和技术会议到底给了我们什么
很多开发者觉得参加技术会议不如自己看文档,这种观点有一定道理,但我不完全赞同。技术会议最大的价值是帮你建立一个坐标感,让你知道当前项目在社区里处于什么位置、别人已经在哪个方向走得更远。Pulsar虽然开源时间不短,但相比Kafka这类更早走向大众的组件来说,社区里的实践沉淀还是相对分散。一个集中交流的机会能把那些散落在不同公司的实战经验集合起来,对一个正在选型或者正在深度使用Pulsar的团队来说,是查漏补缺的成本最低的途径之一。
同时,开源项目的版本迭代非常快,哪怕只有半年不关注,再回来看就会发现API或配置都有了不少变化。技术会议和社区活动的议程通常可以帮你快速建立对最新版本核心变化的认知,在闲暇时再自己动手跑一跑新特性,能避免团队始终落在一个过低版本的误区里。
5.2 从社区用户到深度参与,阶段不同玩法也不同
在Pulsar社区里,不同阶段的参与者获得的收益是明显不同的。刚接触时最好以应用为主,多动手写demo,把Pulsar和自己的业务场景结合起来,验证架构上的假设。使用了一段时间之后,如果遇到文档上找不到答案的问题,可以把查找范围扩大到社区或源码层面,甚至直接看GitHub的提交记录和相关issue。这里有一个非常实用的办法,就是搜索issue的时候不要只看问题和官方回复,还要关注后续代码提交有没有提到修复或新增回归测试,那些往往隐藏了对底层机制的解释。
深度参与社区之后,你会逐渐发现单纯以用户身份接触Pulsar,和在社区共建视角下看Pulsar,完全是两个层次。用户视角关心的是功能能不能满足需要,社区视角关心的是这个项目未来走向哪里。Pulsar的多个模块,比如Broker、BookKeeper、生态连接器、Pulsar Functions,每个方向的水都很深,几乎都能找到值得长期投入的研究点。如果团队有足够的场景和人力,完全可以考虑反哺社区,把生产环境中遇到的真实问题和自定义的优化以提案或补丁的形式贡献回上游。这件事对公司本身也是一种品牌和招聘层面的长期投资。
6. 我的体会与建议
如果只看技术评测文章,很可能会形成一种印象:Pulsar哪里都好,Kafka似乎已经落后。但实际做过生产选型的人会明白,架构能力过剩照样会带来额外的运维成本,技术选型最怕的不是“这个技术不够好”,而是“这个技术是否刚好契合我的业务阶段”。Pulsar在弹性、存储成本、多租户隔离、多地域容灾这些维度上确实有鲜明优势,但你得先想清楚自己是不是已经有这些痛点,正如我前文所说,只有亲身把玩和调优过,才能真正领会这些设计的取舍。
我在自己的项目里第一次部署Pulsar的时候,最深刻的体会是:它的架构在纸面上非常优雅,但真正的复杂度在于基础设施组件变多了。原来用Kafka时只需要关注Broker,现在要同时管理Broker、Bookie和元数据服务,还要处理它们之间的协同问题。如果团队的基础设施能力相对薄弱,建议从托管服务或容器化方案起步,不要一上手就维护一套物理机集群。
最后给一个很实用的扩展建议:把Pulsar的分层存储用起来。即使你现在数据量还没有大到必须卸载历史数据的程度,也可以先在测试环境把它的配置跑通,把对象存储的接口调好。这个能力带来的价值可能不会在第一天体现,但等到数据量和存储成本成为显著问题时,你会发现它让你在容量规划上拥有了极大的灵活性。消息中间件的选型与落地没有银弹,它的真实价值是在一条条消息的流转中被验证出来的,这也是我始终主张动手实践的原因。
