从MQ到Pulsar:架构原理、生产实践与排障指南 📅 发布时间:2026/9/7 21:10:02 👁 浏览次数: 第一次看到活动标题里那句 “Make MQ Great Again”我其实愣了一下随后决定必须去现场看看。MQ 这些年有点委屈它是分布式系统里最不能挂的组件却又常常是最没存在感的组件。平时没人夸它稳定一出问题所有人第一时间都指向它。COSCon‘25 和 Pulsar Developer Day 2025 放在一起明面上是两个社区联合办会实际上是想把“消息中间件到底重不重要”这个问题重新摆到桌面上。两天听下来我最大的感受是MQ 不仅没有过时反而在云原生时代变得越来越重要。这篇文章不是对 PPT 的逐页复述我只写自己印象最深的观察和思考包括 Pulsar 架构到底该怎么学、MQ 装完管理后台打不开这种高频问题怎么排查、以及消息链路里 requestId、routingKey、消息意图这些生产细节。同时也聊聊我在会场上和同行交流时捡到的一些实在经验。如果你正在做消息平台、中间件运维或者正考虑从 Kafka、RabbitMQ 迁到 Pulsar这篇内容应该能派上用场。1. 为什么 COSCon 和 Pulsar Developer Day 要放在一起办1.1 两个活动两种互补的视角COSCon 是国内开源圈覆盖面很广的年度聚会会场里什么项目都有从操作系统到云原生再到 AI 框架五花八门。Pulsar Developer Day 则聚焦 Apache Pulsar 这个具体项目从议题设置到演讲嘉宾都围绕 Pulsar 生态展开。两个活动放在一起对普通参会者最直接的好处是一次差旅能同时看到横向和纵向两个维度的内容。横向是在大峰会上看到开源生态里那么多项目各自在解决什么问题纵向是跟着 Pulsar 的老玩家把一条技术线聊透从架构原理一路聊到生产故障。我自己的体会是只参加大峰会容易“听个热闹”议题太泛人太多出门就忘只参加垂直项目大会又容易“圈地自萌”翻来覆去还是同一批人讨论的问题也越来越小众。COSCon 和 Pulsar Developer Day 联动刚好补上了彼此的短板。现场确实有不少原本只是来逛 COSCon 的工程师因为对“消息队列为什么值得专门开一场会”感到好奇走进了 Pulsar 的专场。这种“意外引入”对项目社区来说比在垂直渠道里反复触达老用户有效得多。1.2 议程背后的三层结构我粗略把两天的内容归纳成三层。第一层是“认识 MQ”面向刚接触分布式系统的人讲消息队列解决了什么问题、和 RPC 有什么区别、异步和解耦的价值在哪里。第二层是“深入架构”面向已经在用 Pulsar 但对原理一知半解的工程师重点讲存储、计算、元数据如何协作各种订阅模型有什么区别。第三层是“生产实战”面向真正维护消息平台的人讲多集群容灾、成本治理、故障排查和性能调优。这个设计看起来顺理成章但实际操作中特别容易失衡。技术类活动最常见的毛病是两个极端要么太浅全是概念和趋势展望没有能带回去用的东西要么太深默认所有听众都懂源码细节新手当场劝退。这次活动在三个层次之间拿捏得比较稳下午的议题普遍比上午硬核愿意深挖的人可以留下来继续听觉得太深的听众也不会被前面几个小时的“无意义铺垫”劝退。我甚至觉得这种“上午科普、下午实战”的结构本身就值得其他技术社区参考。1.3 参会别只为了收集 PPT我见过不少人来技术大会全程举着手机拍屏幕回去之后再也没打开过那些照片。这里有个误区PPT 是讲给全场听众的真正值钱的信息往往在会后提问和茶歇交流里。这次我在 Pulsar 专场遇到一个做金融系统的工程师上来就直接问“我们想从 Kafka 迁到 Pulsar最大的坑到底在哪”。这种具体问题比任何精美 slide 都值得记录。所以我的建议是去技术大会之前先把手里的 MQ 问题列一个清单哪怕只有三条。比如“broker 频繁 full GC 怎么定位”“消息积压到一百万条先扩容还是先排查消费端”“副本写入延迟抖动怎么查”。带着具体问题去听你会发现很多演讲正好能在某个角度回答你的疑问哪怕没有人正面回答你也能在茶歇时把问题抛给演讲者或其他参会者这种面对面的交流才是线下活动最不可替代的部分。2. 被问得最多的问题Pulsar 架构到底该怎么学2.1 为什么 Pulsar 的架构让很多人卡住现场和网上热度都很高的一个问题是“pulsar架构详细学习”。我观察到一个现象很多人在 Kafka 上写了好几年代码业务逻辑玩得很熟但一转到 Pulsar 就不会用了。原因不是 Pulsar 更复杂而是它的架构思路和 Kafka 有本质区别。Kafka 是分区日志模型存储和计算绑在同一个 Broker 进程里每个分区既负责接收消息也负责把消息写到本地磁盘。Pulsar 则把“计算”和“存储”拆开了——Broker 只负责消息路由、消费状态管理和权限校验真正持久化数据的是另外一层 BookKeeper 集群。打个比方Kafka 像一家前厅后厨连在一起的小饭馆客人多了前厅坐不下你只能把整个店面一起扩大。Pulsar 像连锁餐饮集团前厅接待和后厨仓库完全分开中午排队的人多了你可以只多开几个前厅后厨仓根本不用动。这个架构差异不是炫技它直接影响扩容成本、资源利用率和故障恢复速度。很多人第一次接触 Pulsar 时觉得“多了一层东西好重”其实是还没理解这层抽象换来的运维灵活性。2.2 一条消息从发到收到底经过了哪些组件与其盯着架构图看不如跟一条消息走一遍完整路径。Producer 连接 BrokerBroker 先做鉴权和流控然后把消息写入 BookKeeper 的某个 Ledger 里与此同时Broker 会根据订阅模型维护消费者的消费位点也就是 Cursor。Consumer 主动从 Broker 拉消息Broker 再从 BookKeeper 里把数据读出来通过二进制协议返回给消费者。这里有一个非常常见的误解很多人以为 Pulsar 的消息是存在 Broker 上的因为用起来和 Kafka 实在太像了。实际上 Broker 只承担转发和调度不落盘。消息一旦进入 Pulsar最终躺在 BookKeeper 里由 BookKeeper 负责多副本复制和分段存储。所以 Pulsar 的扩容可以拆开做计算资源不够就水平扩展 Broker存储容量不够就扩展 BookKeeper 节点。这个特性在生产环境非常有价值因为很多时候你只是突然需要更多存储而不是真的需要更多计算。组件一句话职责常被误解的点Producer / Consumer应用侧的消息发送与消费应用不直接与 BookKeeper 交互Broker无状态的服务层不存储消息数据只做路由和调度BookKeeper持久化存储层不是“Pulsar 专用的 Kafka”它是独立的存储系统ZooKeeper元数据与协调存储主题、租户、Broker 注册等元数据2.3 理解 Pulsar关键是先理解订阅模型Pulsar 的订阅模型是它和 Kafka 拉开差距的地方。Kafka 里消费者用 Consumer Group 来组织一个分区在同一个 Group 里只会被一个消费者实例消费。Pulsar 的 Subscription 更进一步同一个 Topic 可以同时挂多个不同的订阅每个订阅互不影响各自拿到全量消息。对于“一份消息要广播给多个团队”的场景Pulsar 的表达非常自然不需要像 Kafka 那样复制一份 Topic 再重新建一套链路。具体到单个订阅内部Pulsar 提供几种模式。Exclusive 是独占消费一个分区只有一个消费者适合需要严格全局顺序的场景比如账户流水。Shared 是共享消费多个消费者共同分担消息吞吐高但顺序不保证适合日志处理、通知推送这类场景。Key_Shared 是介于两者之间的方案按照消息 Key 把相同 Key 的消息路由到同一个消费者既能保证某个维度内的顺序又能做到水平扩展。现场有人问什么时候用 Key_Shared我举了个例子订单状态流转同一个订单 ID 的消息必须被同一个消费者处理否则会出现状态回退但不同订单之间天然没有顺序约束这时候 Key_Shared 就是最合适的模式。2.4 我的学习路径建议先跑通再原理最后源码在会场和新手交流的时候我反复说一句话先跑通再讲原理最后才看源码。很多人一上来就搜“Pulsar 源码解析”然后被 Ledger、Entry、Fencing、Ensemble 这些底层术语劝退实际上完全没必要。合适的顺序应该是这样的第一步用 Docker 把 standalone 模式跑起来用命令行工具敲一遍 producer 和 consumer 的命令确认消息能正常收发。这个过程一小时内能完成能建立最直接的感性认知。第二步打开官方文档的 Concepts 和 Architecture 章节一边看一边对照刚才跑通的命令搞清楚消息到底写在哪个组件里。第三步才去看源码而且只看和自己工作相关的模块。比如你关心消费失败重试就去看 Broker 侧的 Dispatcher 实现你关心消息轨迹就去看相关插件和接口。说实话对大多数业务团队来说读源码不是必须的。真正需要读源码的人是要修改 Pulsar 内部逻辑或者做深度二开的场景。普通使用场景下把 Topic、Subscription、三种订阅模式的原理搞明白再学会看监控指标就足够应付日常开发了。3. MQ 装完管理后台进不去这件小事值得讲清楚3.1 这个搜索词的热度和现场被问的频率完全一致“mq安装后管理后台无法进入”这个问题的热度我在现场感受到了。茶歇时至少有四位朋友问过类似的话“我用 Docker 把 Pulsar 拉起来了端口也映射了为什么浏览器打开 IP:8080 就是打不开后台”首先要澄清一个很多新人不知道的事实Apache Pulsar 官方镜像默认不带图形化管理后台。你在浏览器里访问 8080 端口正常情况下看到的是一个 REST API 响应或者一个简单的服务信息页不是像 RabbitMQ 那种默认自带的管理界面。Pulsar 真正意义上的可视化后台是 pulsar-manager需要单独部署。很多人在本地装完 Pulsar 之后满世界找后台入口找不到然后怀疑自己哪里配置错了其实只是“默认没有 UI”而已。3.2 我总结的“三步定位法”如果你确认已经部署了管理后台还是进不去我建议按下面的顺序排查不要一上来就重启容器。重启解决不了问题只会掩盖状态信息。第一步确认端口。Pulsar 的 8080 是 Broker 的 HTTP 服务端口6650 是客户端使用的二进制端口。如果 docker 端口映射只写了 6650:6650那网页自然打不开。用ss -tlnp | grep -E 8080|6650查看本机端口有没有监听。这里还要注意容器内服务绑定的是0.0.0.0还是127.0.0.1如果绑在127.0.0.1宿主机外部访问不到。第二步确认服务状态。Pulsar standalone 启动后直接访问curl http://localhost:8080/admin/v2/clusters看有没有 JSON 数据返回。如果返回一个集群列表说明 Broker 服务和 API 都是正常的问题肯定出在管理后台组件本身如果连接被拒绝就要回到日志去查服务为什么没起来。这一步能把“后台问题”和“服务问题”快速分开不用两头猜。第三步查日志。这一步最容易被忽略。很多人习惯性看“服务有没有起来”却从不看日志里到底报了什么错误。Pulsar 启动阶段常见的隐藏问题包括ZooKeeper 没就绪、BookKeeper 磁盘空间不足、JVM 堆过大导致进程 OOM。这些问题在启动日志里都会明确打出来早看早省事。如果用的是 Docker用docker logs pulsar -f直接看容器输出比进容器里翻文件方便得多。3.3 两个特别隐蔽的坑第一个坑是内存配置。Pulsar 默认的 JVM 堆参数是按照大内存机器预设的如果你本机只有 4G 内存直接跑 standalone 很容易把内存吃光现象是容器起来一两分钟就挂或者一直处于重启状态。解决办法是显式设置PULSAR_MEM环境变量把堆调小比如PULSAR_MEM-Xms512m -Xmx512m -XX:MaxDirectMemorySize1g第二个坑是认证配置。如果集群开启了 Token 认证但管理后台的登录账户没有正确授权浏览器访问时会不断重定向到登录页看起来像“进不去后台”。这种问题特别容易被误判为网络故障。建议在首次排查时先用 curl 带 token 访问admin/v2/brokers/health如果带 token 能通、不带 token 返回 401那就是认证配置问题不是网络问题。另外开启认证时 pulsar-manager 的超级用户需要显式配置这一步很容易漏官方文档写了但实际配置时经常被跳过去。给想快速体验的朋友一个基础配置参考version: 3.8 services: pulsar: image: apachepulsar/pulsar:3.3.0 container_name: pulsar ports: - 8080:8080 - 6650:6650 command: bin/pulsar standalone这个配置只适合本地学习用生产环境不能直接照抄。生产场景下 Broker 和 Bookie 要分开部署集群模式、存储计算分离、认证授权都要重新设计。把它当做一个“跑通前提”就好。4. 生产环境必修课requestId、routingKey 与消息意图4.1 requestId 是排查问题时的“串联线”现场听到好几个案例归根结底都是一句话线上消息丢了但完全不知道丢的是哪一条。如果消息体里没有 requestId你连问题的起点都找不到。requestId 就是一条消息的身份证从入口生成之后要贯穿到所有相关的日志、消息体、回调处理里。具体做法一般是在消息的 property 里放一个 requestId比如 Pulsar 消息的getProperties().get(requestId)同时在应用的日志框架里用 MDC 把同一个 requestId 打进去。这样无论是查服务日志还是看消息轨迹都能用同一个 ID 串联整条链路。活动上有位工程师说得特别实在有了 requestId排查消息重复投递、丢失、乱序这些问题至少能先缩小到某一条具体消息而不是在一堆日志里大海捞针。4.2 routingKey决定消息往哪走的“地址”很多从 RabbitMQ 切换过来的朋友第一反应是找 Pulsar 里的 routingKey 概念。这里要提醒一下Pulsar 没有完全等同的 routingKey它是通过 Topic 来表达目的地通过 Message Key 加 Key_Shared 订阅模式来实现类似路由的效果。Message Key 是发送消息时附带的一个字符串Pulsar 会根据 key 的哈希值把消息分配给对应的消费者。实际使用中key 设计得好不好直接影响消息顺序和负载均衡。订单场景里把订单号作为 key就可以保证同一个订单的所有消息始终被同一个消费者处理。但如果 key 设计得过粗比如所有消息都用同一个固定 key那就等于退化成独占模式性能上限被压得很低如果 key 设计得过细每个消息的 key 都随机又会导致消费端负载不均。这个度需要根据业务数据的自然维度来把握没有统一答案但“先聚合业务维度再考虑均衡性”是基本方向。4.3 消息意图决定重试、死信和补偿策略“intent”这个词在现场被多次提到我理解它在 MQ 里代表的是“这条消息的业务意图是什么”。同样是发一条消息“扣减余额”和“发送通知”对可靠性的要求完全不同。扣款消息不能丢、不能重复、不能乱序任何一条出错都是资损通知消息可以允许少量丢失但对延迟的容忍度更低同步缓存类消息则更看重最终一致对时效不敏感。所以在设计阶段就应该把消息意图显式地表达出来而不是等出了问题再去猜。可以把 intent 做成枚举放在消息的 property 或 Topic 命名上。Topic 命名我比较推荐“业务域-动作”的格式比如order-payment-command和order-payment-event前者是命令消息后者是事件消息。看到 Topic 名就能知道消息是命令还是事件、属于哪个业务域对应的重试策略和死信策略也会有依据。现场有人带着自己的命名规范来交流我对比了一圈发现大部分踩坑都是因为 Topic 命名太随意业务域和意图完全没有体现出来。4.4 消息平台排查问题时的顺序感这一节算是我自己的一点体会。消息平台出问题最怕的不是问题本身而是排查的时候没有顺序。我的习惯是三步走先看 requestId 能不能串联链路再看 routingKey 是否把消息放到了预期的队列或分区最后看消息 intent 对应的重试、死信策略有没有按预期生效。三步走完大部分“消息丢了”“消息积压”“重复消费”的问题都能定位到具体环节。有一点想说清楚很多人习惯在消息系统出问题时马上去翻消费者代码但消费者代码只能解释“为什么没处理”解释不了“消息到底走没走到这里”。MQ 的排查一定要从源头到终点按链路看跳着查只会越查越乱。如果你连消息是否成功投递到 Broker 都不清楚就不要先去猜消费端逻辑有问题。5. 从圆桌和展区感受到的三个趋势5.1 多集群容灾已经不是大厂专属话题以前聊多集群容灾默认是头部互联网公司的事情中小企业用单集群就好。但这次活动上至少两场分享都在讲低成本的多集群容灾方案。不是一定要搞双活而是“先在另一个可用区或者另一个机房有一份可切换的备份”。有个观点我特别认同多集群很多时候不是技术题而是成本题和管理题。选什么方式做跨集群复制、多久做一次切换演练、怎么验证另一边的数据完整性这些比“怎么搭一个集群”更值得花时间。Pulsar 的跨地域复制功能本身做得比较成熟可以在多个集群之间配置复制消息会异步同步过去。但真正难的是故障切换时的决策什么时候切、怎么切、切完怎么回切。现场有人分享的演练经验是每个月强制做一次切换让全链路的人都熟悉流程。这样真正出事故的时候不会出现“脚本不会跑”“忘了 DNS 怎么改”这种低级问题。5.2 存储成本正在超过计算成本Pulsar 的核心优势是存算分离但存算分离也改变了成本结构。以前 Kafka 时代成本随着 CPU、内存、磁盘一起线性膨胀扩容就是加机器。Pulsar 把存储放到 BookKeeper 之后计算节点可以缩得很小但存储层的副本数、消息保留策略、冷热数据归档方式直接决定了账单。现场有位做运维的朋友算过一笔账如果保留了三副本又长期不设置清理策略存储成本会比同规格 Kafka 的本地盘方案高出不少。所以 Pulsar 落地时有几个配置值得重点关注消息保留时间、积压消息的过期策略、BookKeeper 的自动清理频率以及是否有条件把老数据分层存储到对象存储上。这些“琐碎”的配置长期看比选型本身更影响成本。很多团队做 Pulsar 选型时只看功能和性能忽略了容量规划结果用了几个月后发现存储成本超出预期再回头调已经晚了。5.3 生态兼容正在降低迁移门槛Pulsar 不是要求你把整个客户端体系全改一遍才能迁移的平台它提供了 Kafka 协议兼容层也就是 KOPKafka On Pulsar。这意味着原有的 Kafka 客户端和大部分代码基本不用动就能连到 Pulsar 集群上先把业务跑起来再逐步把客户端迁移到 Pulsar 原生协议。这个策略对生产环境非常友好大大降低了切换的决策成本。当然长期使用我还是建议逐步迁移到原生协议因为只有原生协议才能最完整地发挥订阅模型、消息轨迹等特性的价值。KOP 更大的意义在于给团队一个“安全垫”让切换不至于变成孤注一掷。我在现场的感受是很多团队对 Pulsar 动心已久但一直不敢迈出第一步KOP 这类兼容层正好解决了这个顾虑。6. 对想入坑 MQ 和 Pulsar 的人说几句实在话6.1 从“我会用”到“我理解”中间差的是异步思维很多人说自己会 MQ其实只是会用 API。发消息、收消息、配个重试看起来都会但一遇到消息乱序、重复消费、死信堆积就懵了。这不是操作问题是思维方式还没有转过来。MQ 本质上把同步调用变成异步事件流你要接受一个事实消息可能晚到、可能重复、可能乱序。把这些不确定性纳入系统设计而不是假装它们不存在这才是真正理解消息队列的开始。6.2 我推荐的 Pulsar 学习路径结合这次活动如果你现在还是新手我建议按下面这条路走顺序很重要先跑通 standalone 模式用命令行收发消息建立直观印象。再读官方文档的 Concepts 和 Architecture把各个组件职责搞清楚。重点理解 Subscription 的几种模式以及各自适合的业务场景。用 pulsar-manager 练习管理后台和消息轨迹熟悉可视化操作。最后再根据工作需要看源码只看相关模块即可。如果你已经能说出“Pulsar 的消息是存在 BookKeeper 里的Broker 只是调度层”这句话恭喜你已经超过大部分“用过 Pulsar”的人。很多人用了一两年都未必意识到存储和计算是分离的。最后再分享一个这两天最让我受用的判断标准不管用什么消息队列在设计任何一条消息链路时只要回答不上来“这条消息丢了会怎样”“这条消息重复了会怎样”那链路大概率还有隐患。把这两个问题想清楚比多看十篇架构分析都管用。Make MQ Great Again说到底就是从这些最基础的问题开始重新把消息中间件当成一个值得认真设计的基础设施来对待。