做消息中间件选型这些年,我有一个很深的感受:很多时候团队纠结的并不是“用哪个”,而是“为什么换”和“换了之后怎么把老问题一起带走”。这次COSCon‘25同场活动里,Pulsar Developer Day的议程一出来,我第一时间就翻完了,说实话,这个活动对正在Kafka、RabbitMQ、RocketMQ之间犹豫的团队来说,是一个很好的观察窗口。Apache Pulsar作为一个存算分离架构的消息中间件,这几年在云原生场景下的热度一直没降过,它到底解决了哪些别人解决不了的问题,以及开发者社区现在最关心什么,这篇文章我想结合这份议程,把我自己的理解、踩过的坑、还有实际用下来的体会一次说清楚。
先说结论:如果你所在团队的消息量已经大到开始频繁调整分区、扩容集群、处理堆积告警,或者你正在为“削峰填谷”以外的需求——比如事件重放、多租户隔离、跨地域容灾——寻找答案,那Pulsar值得你花一个下午认真研究。这篇文章适合刚好想入门消息中间件的新手,也适合已经跑了几年Kafka、正打算对比迁移方案的架构师。
1. 消息中间件选型背后的真实痛点
很多开发者第一次接触消息中间件,都是从“我要解耦”开始的。订单系统要通知库存系统、积分系统、物流系统,总不能一个接口一个接口地同步调吧,于是消息队列上场。但用着用着会发现,消息中间件这东西,难的不是写代码收发消息,而是后面那一堆运维问题。
1.1 消息中间件到底解决了什么问题
往大了说,消息中间件干的是三件事:异步解耦、流量削峰、数据分发。异步解耦让系统之间的依赖不再是一根硬邦邦的HTTP链子;削峰填谷能让上游瞬时流量先进队列,下游根据自己的处理能力慢慢消费;数据分发则是把一份消息扇出给多个系统,各自按需消费。
但这里有个容易忽略的点:前两点是“基础设施”层面的价值,第三点才是区分产品能力的试金石。比如Kafka天生适合做日志类的大吞吐顺序追加,但让多个业务团队共享同一个Kafka集群,分区数量一多,运维就开始头大;RocketMQ在事务消息和延迟消息上做得顺手,但在跨地域复制和分层存储上,能力边界也比较明显。
我见过不少团队,初期用一个开源MQ就够,等业务量上来,开始遇到一堆“别人家没有”的问题:扩容要动分区数,分区数定了就定死了;消费堆积想重放历史消息,发现消息早被清了;不同部门想共用一套集群,隔离和配额又说不清楚。这些问题的根源,往往不是某个中间件“不好”,而是架构模型对云化、规模化场景的支持力度不够。
1.2 从Kafka到Pulsar,选型到底在比什么
我们做一个不吹不黑的对比。Kafka的核心模型是一个分区一个日志文件,分区在broker上是有状态的,Broker之间靠复制协议同步数据。这个模型决定了Kafka的吞吐量可以做得非常漂亮,但也带来了两个硬约束:第一,分区数上去了,每个分区的文件句柄和内存开销会摊薄整体性能;第二,Broker故障时,分区Leader切换依赖ZooKeeper协调,虽然现在KRaft模式正在改进,但对老运维来说,那个“重启一个Broker要小心翼翼”的手感还是记忆犹新。
Pulsar走的是另一条路。它把服务层和存储层拆开了:Broker只管收发消息、处理协议、做流控,是无状态的;真正的数据存在BookKeeper里,BookKeeper的每个存储节点只管append日志,并且按段(ledger)滑动滚动。Broker可以随时扩缩容,存储节点可以独立扩容,两者互不拖累。
这种存算分离的架构,放在云原生环境下就是降维打击。K8s里跑Pulsar,数据节点和计算节点分开扩,资源利用率能算得明明白白;而Kafka在K8s里跑,StatefulSet那套折腾过的兄弟都懂。
1.3 为什么Pulsar的社区活动值得关注
消息中间件领域这几年的开源活动不少,但Pulsar Developer Day这类活动有一个独特价值:它把“内核开发者”和“一线使用者”放在同一个会场里。你既能听到社区核心committer讲最新功能背后的设计思路,也能听到美团、腾讯、智联招聘这些真实用户讲他们踩过的坑和沉淀的实践。
这和我们平时看的博客性质不一样。博客讲“怎么做”,开发者日的分享更多讲“为什么这么做”和“做的时候遇到了什么”。比如一个session讲Pulsar的IO隔离,光看文档你知道Broker会把读写流量分开,但线上流量突增时读路径被写路径拖垮到底怎么排查,这种经验只有实战过的人才讲得出来。
所以这篇解读,我打算把议程里的技术主题拆开,并结合我自己的实践,把那些藏在标题背后的内容点给你看。你去了能听懂聊什么,不去也能知道这个圈子现在在关注什么。
2. Pulsar核心特性拆解:不只是又一个消息队列
很多第一次接触Pulsar的人,第一反应是“又一个消息队列”。这个印象不准确。Pulsar的设计目标其实比“队列”大得多,它想做的是统一的消息流平台——队列模型、流模型、存储模型都能在一个系统里体现,而且所有这些能力都建立在一套可扩展的底层存储之上。
2.1 存算分离架构到底解决了什么问题
用一个生活类比来说。传统消息中间件像一家餐厅,每个服务员既要记菜名(计算),又要上菜收桌子(存储),客人一多,服务员累死,加人也没用,因为餐桌就那么多。Pulsar的存算分离就像后厨和前厅分开,前厅服务员可以随便加,后厨的灶台也可以单独扩,各管各的事。
具体到技术层面,Pulsar的Broker是一个无状态的接入层,它主要干三件事:接收生产者的消息、把消息写入BookKeeper、把消息分发给消费者。由于Broker不持有数据,水平扩容就是加机器这么简单,不需要重平衡数据。消息数据在BookKeeper里按ledger存储,ledger是一个追加写的日志段,写入会同步刷到多个bookie节点上,保证副本数。
这个模型带来的第一个红利是扩容体验。Kafka扩Broker通常意味着分区重分配,数据要在节点间搬来搬去,窗口期要盯监控。Pulsar扩Broker,节点加完就完事,新连接自动被负载均衡过去,线上业务没有任何感知。
第二个红利是IO隔离。Pulsar的读写路径在存储层天然分开,写路径走BookKeeper,读路径由Broker直接从存储里读取并缓存,这意味着你不需要像Kafka那样把整个分区的数据都塞在page cache里才能保证读取性能。读多写少的场景,Pulsar的缓存命中率会比Kafka好控制。
2.2 多租户与分层存储:让运维松口气的设计
微服务架构普及之后,公司内部的消息集群通常不是只有一套业务在用。以前用Kafka,一个团队想共用集群,就得开一堆Topic,然后靠命名规范加上权限控制来“假装”隔离。Pulsar原生支持多租户,租户(tenant)是顶层隔离单元,每个租户下面可以建多个命名空间(namespace),命名空间里可以配置各自的存储配额、消息保留策略、权限角色。
这意味着什么?意味着不同部门之间可以真正共享一套Pulsar集群,但每个部门只能看到自己的命名空间。配额超了,监管告警精准到租户;权限配置可以精确到“这个应用只能写这个Topic,只能从这条消费者订阅里读”。对于有几十条业务线的中型公司,这是实打实的运维减负。
再说分层存储。Kafka的日志保留时间长了,磁盘成本压不住;短了,需要重放历史数据的场景又不满足。Pulsar的架构天生方便做分层:消息写入BookKeeper后,超过一定时间或大小的数据段会自动卸载到S3、GCS这类对象存储里。消费端还是按老Topic、老订阅来读,数据在哪个层对用户透明。
这个能力对一个做数据回放的核心系统真的太管用了。比如做风控或审计,需要查三个月前的某笔订单的事件流,Kafka大概率已经不给你留了,对象存储里有的话你已经找不到入口了;Pulsar配合分层存储,把“无限留存”变成了一个配置项,而不是一个运维事故预案。
2.3 为什么“消息中间件”正在向“流平台”演进
现在的业务系统对事件数据的需求越来越多样。不仅今天要消费,明天可能还要重放;不但要实时算,还要能回溯到某个历史时间点重新算一遍。如果一个系统只能“读一次”或“最多存几天”,它本质上只是管道,不是数据资产。Pulsar把消息默认持久化,而且支持消息重播(rewind),消费者可以按时间或按消息ID重新读取,这让“事件流”变成了一个可以反复利用的数据底座。
另外一个被很多团队忽略的点是Pulsar的订阅模型。它同时支持独占订阅、共享订阅、故障转移订阅和Key_Shared订阅。一个Topic,既可以被一个消费者独占拉取形成有序消费,也可以被一组消费者共享分摊负载,同一份数据还能同时喂给实时计算任务的多个实例。这种灵活的消费模型,让Pulsar既能当削峰填谷的队列用,也能当实时流计算的数据源用。
线上会话保持(比如WebSocket网关协调、分布式锁协调)是Pulsar生态里另一个高频使用场景。Pulsar早期版本就内置了基于游标管理的跨地域复制和通过Broker分发实现的“纯粹分布式”消息队列能力,现在配合Pulsar Functions、Pulsar IO(连接器框架),它离“统一流处理平台”的定位越来越近了。
3. Developer Day议程背后的技术看点
这次Pulsar Developer Day的议程,从主题分布上能看出社区现在的关注重心。结合我自己了解的历次活动和社区讨论,这几个方向大概率是大家最想听、也最能带回去直接用的。
3.1 核心链路:生产到消费的端到端性能调优
开发者日里最难“水”的一类分享,端到端性能调优一定排在前面。一个消息从Producer发出来,到Consumer拿到手,中间要经过网络传输、Broker接入、BookKeeper落盘、Broker分发这几个环节。任何一个环节出现短板,整个链路的延迟和吞吐就上不去。
常见的新手问题是从Producer端就埋雷。比如发送模式用了同步发送,每条消息都等确认,吞吐自然上不来;比如批量参数设置不合理,客户端攒不够batchSize就一直不发,延迟假高。又比如生产者没有做消息去重,课上讲幂等生产者的时候没注意,业务重试时消息重复投递,下游消费端又没做幂等,线上数据就花了。
消费端的问题也不太一样。共享订阅的消费者数量设置不合理,分区数不够,消费者数量再多也没用;单条消息处理时间太长,又没有开并发拉取,整个订阅的消费速率就被最慢的那个消费者拖住了。诸如此类的问题,听有实战经验的人带着压测数据和监控曲线讲一遍,比自己回去看文档要省好几天时间。
3.2 实践案例类分享,最值得关注的三个方向
结合Pulsar社区这几年的落地情况,案例类分享最值得关注的通常是这三个方向。
第一个是“从Kafka迁移到Pulsar”的路径复盘。这个方向的分享者一般会讲清楚两件事:如何通过Kafka兼容协议降低迁移成本——Pulsar的Kafka API兼容层可以让你现有的Kafka客户端不换语言、不改代码,只改连接地址就接上Pulsar集群;以及迁移过程中消息怎么平滑切换、消费位点怎么同步、双跑期间怎么对数。这类内容对有存量Kafka系统的团队几乎是刚需。
第二个是“大规模集群运营的稳定性实践”。Pulsar集群上了几十个Broker、几百个Bookie之后,运维复杂度是几何级上升的。Bookie的JVM参数怎么调、ledger的写入分布怎么均衡、Broker和Bookie的机架感知怎么配置才能让副本分散到不同故障域,这些都是文档里不会细讲、论坛里有人问但答案不全的东西。
第三个是“实时数仓和事件驱动架构里Pulsar的角色”。现在很多公司把Pulsar当成实时数据管道的数据基座,上游数据库变更通过CDC进Pulsar,下游Flink算完再回写Pulsar,最后供在线服务查询。这类分享的看点不在Pulsar本身,而在它如何和Flink、CDC工具、数据湖做整体编排,听完对整个数据链路的理解都会有提升。
3.3 生态与工具链:连接器、Kafka兼容和云原生部署
Pulsar能走多远,很大程度取决于生态。Pulsar IO连接器体系这些年进步很大,内置了几十个Source和Sink,像Debezium CDC、JDBC、Elasticsearch这些常用连接器都能直接配置使用。开发者日常如果不想自己写连接器,基本可以做到配置式接入。
Kafka兼容这块,对很多团队来说几乎是“救命稻草”。不管社区怎么讨论架构先进性,公司现有的存量代码就是Kafka客户端写的,重写一套的成本动辄几周。Pulsar提供了一版Kafka协议兼容handler,把Pulsar Broker伪装成Kafka Broker,现有Kafka客户端只需改bootstrap.servers就能接入。这个兼容层的细节做得越深入,迁移阻力就越小,所以开发者日里只要涉及生态的session,总是坐满人。
云原生部署也是绕不开的话题。现在新项目很少有愿意自建机房跑虚拟机集群的了,大家都在看K8s里的表现。Pulsar官方有一套基于Helm的部署方式,也用到了cert-manager做证书管理。用Helm部署Pulsar不算难,难的是之后怎么把Broker的优雅停机和BookKeeper的持久化存储卷在K8s环境里调稳。这个方向的分享能给出大家普遍踩过的坑和解决路径,价值是比较实在的。
4. 实操经验:把Pulsar用到生产环境的几个建议
议程再丰富,最后还是要回到自己的环境里把集群跑起来。下面这些建议是我自己从测试环境到生产环境反复折腾后,觉得最值得分享的几条。
4.1 部署环境和初始参数怎么定
如果你只是在本机跑个Pulsar体验一下,用Docker一条命令就能起个standalone实例,这没什么好说的。真正要进生产,第一个要明确的就是Broker和Bookie要分开部署,别图省事装在一台机器上。混布虽然省机器,但JVM堆、page cache、磁盘IO互相抢资源,一旦出问题排查成本翻倍。
BookKeeper的参数里,有一个很容易被忽略的设置是journalDirectory和ledgerDirectories,前者放预写日志(journal),后者放实际消息数据。生产建议把两者放到不同的物理磁盘上,这样可以避免写日志和写数据互相争抢磁盘IO。之前就有团队图省事都放一块盘,服务一开大流量就频繁毛刺,换盘后立刻稳定了。
Broker端的JVM堆不要盲目给大。Pulsar Broker是个无状态接入层,堆内存主要给缓存和游标管理用,堆给太大反而增加GC压力。一般根据带宽和连接数估算,8G到16G之间比较常见,具体的值要看你的连接数量和Topic数量来压测验证。另外,managedLedgerDefaultEnsembleSize、managedLedgerDefaultWriteQuorum和managedLedgerDefaultAckQuorum这三个参数决定了消息的副本数,默认是2、2、2,如果你的数据可靠性要求高,建议改成3、3、3,但也要注意磁盘空间会多三分之一。
还有一个特别容易踩的坑是Broker和Bookie的时区配置。Pulsar的某些指标、日志以及跟对象存储交互时的路径命名会用到本地时间。如果集群机器时区不一致,轻则日志对不上,重则分层卸载的临时文件清理任务找错对象。这件事虽然简单,但排查起来很费时间。
4.2 常见问题与排查思路
我把自己遇到过的典型问题整理成了一张速查表,按着这个思路排查效率会有明显提升。
| 问题现象 | 常见原因 | 排查思路 |
|---|---|---|
| 生产端持续报超时 | Broker连接数过高或BookKeeper写入慢 | 先看Bookie的journal写入耗时,再看Broker的线程池饱和度 |
| 消费端延迟持续升高 | 消费者处理慢或分区数少于消费者数 | 看消费者fetch速率,确认每条消息的处理耗时 |
| 消息丢失 | 副本数设置过小或生产者是异步发送且未开重试 | 查生产者发送确认模式、检查ackQuorum |
| 主题出现不可读的“系统Topic” | 使用Replicator跨集群复制未清理旧配置 | 检查全局命名空间里的复制器状态 |
| 集群负载不均 | 存储节点ledger分布倾斜 | 用pulsar-admin检查bookie的磁盘使用和ledger分布 |
比如“消费端延迟持续升高”这个场景,我曾经排查过一个问题:消费者的单条消息处理耗时只有2ms,但整体消费速率就是上不去。最后发现是共享订阅的消费者预取(prefetch)条数设置太小,消费者虽然处理得快,但本地buffer一直处于饥饿状态,网络拉取开销成了瓶颈。把prefetch从500调到5000之后,吞吐直接翻了一倍。
还有一个容易让人懵的场景:Topic明明没有消费者,但打开管理界面发现积压消息一直在涨。这大概率是生产端在持续写入,且消息保留策略设为了持续保留。Pulsar默认的消息保留策略只保留一定时间或大小,如果你的命名空间里设置了无限保留,积压增长其实是正常的,需要根据消费需求重新评估保留策略。
另外,如果你用的客户端是旧版本,一定要关注Broker升级时的兼容性。Pulsar对协议兼容有专门的兼容性矩阵,Broker版本和客户端版本差太远,可能会出现消息ID解析异常、订阅游标不兼容的问题。升级前把客户端版本统一升一升,能省掉很多线上故障。
4.3 什么场景我真的不建议用Pulsar
聊了这么多Pulsar的优点,也该说说它的不适用范围。任何技术都有边界,Pulsar也不是银弹。
如果你的业务消息量很小,一天就几万条,团队只有两三个人,对多租户、分层存储这些特性完全没需求,那就没必要为了用Pulsar而用Pulsar。部署一套Pulsar集群需要Broker和Bookie至少各两三个节点起步,资源开销在那里摆着,杀鸡用牛刀不是效率高的选择。这种情况下,用云厂商提供的托管MQ或者轻量的Redis Stream,反而更合适。
如果你的业务消息模型极其简单,就是纯削峰填谷,对重放、多租户、流处理都没想法,那选Kafka或RocketMQ都行,它们在成熟度、社区资料、排查经验上都要更厚。Pulsar的架构优势要在大规模、多团队共享、长留存、跨地域这些维度叠加起来之后才会非常明显,用不到这些维度时,它的复杂度反而是负担。
对于想把Pulsar用在“交易类、强一致消息场景”的团队,也要留个心眼。Pulsar在消息投递语义上提供最多一次、至少一次、有效一次等选择,但有效一次需要通过幂等和去重配合才能实现;事务消息的支持虽然已经有了,但使用场景相对狭窄,不能期待它像数据库事务那样万能。设计系统时,还是要围绕“异步最终一致”来思考,消息中间件不是分布式事务的银弹。
5. 参会前建议带着这些问题去听
如果你已经决定去Pulsar Developer Day现场或关注后续资料,我建议提前准备几个问题,带着问题去听,效率和收获会高很多。
第一个值得关注的问题是:分享里提到的性能优化场景,和我的业务模型是否一致。线上性能问题通常跟消息大小、Topic数量、消费者模式强相关,别人遇到的是大消息批量写,你遇到的问题可能是小消息高并发写,两者的优化方向可能完全不同。听的时候别只记结论,问清楚压测时的消息大小、分区数量、消费模式,才能判断能不能照搬。
第二个值得关注的问题是:兼容层在生产环境到底能承压多少。很多团队关心Kafka兼容协议,尤其是迁移项目。但兼容和替代是两个概念,兼容层能让你API兼容,但是游标管理、流控行为、延迟特征还是有差异。如果分享者有压力测试数据,问清楚在什么规格的集群、什么样的负载模型下测出来的,这比一个空洞的“兼容性良好”要有价值得多。
第三个值得关注的问题是:社区版本的功能演进方向是什么。Pulsar社区一直在推进新功能,比如统一的协议处理框架、轻量级事务改进、BookKeeper的日志压缩优化等。了解这些演进方向,可以帮助你做技术选型时判断风险:你要用的功能在社区版本里是稳定的还是实验性的?维护活跃度如何?要不要引入商业支持?这些判断做对了,能少走很多弯路。
6. 我对Pulsar生态落地现状的一些体会
Apache Pulsar从2018年左右开始被国内大厂采用,到现在海外社区也在持续升温,它的发展路径其实很像所有优秀开源项目共通的路:先解决少数技术领先团队的极端问题,再逐渐把能力产品化、生态化,最终让普通团队也能用上它的好处。
我个人在跑Pulsar集群的过程中,记忆最深的一次经历是配合跨地域复制做灾备演练。两个机房之间同步消息,由于网络抖动导致复制延迟上升,但我发现Pulsar的复制机制不会因为网络抖动把本地消费者拖死——Broker依然正常提供本地消息服务,只是复制积压了一下。这种“故障半径被隔离”的体验,在以往用其他中间件做异地多活时是比较难得的。
还有一个感受是社区资料的质量。中文社区这两年活跃度明显提升,技术文章、用户案例、benchmark数据都越来越多了,有问题去GitHub issues和社区群搜,经常能看到靠谱的回复。对于开源项目来说,社区健康度比单点技术亮点更能决定长期价值,从这一点看,Pulsar的生态是在往好的方向走的。
如果你准备开始评估Pulsar,我最后再给一个具体的小建议:先别急着搭大集群,先用standalone模式跑起来,把生产、消费、重放、多租户这几个操作亲手做一遍,再找一个小流量非核心业务放到共享订阅下跑一两周。感受一下它的消费模型和运维手感,再做决策。技术选型这事,光看文档和评测是不够的,亲手用过的体感,往往才是最后做决定的依据。