news 2026/9/15 0:25:55

消息队列实战(4):RocketMQ 与 Pulsar 架构选型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
消息队列实战(4):RocketMQ 与 Pulsar 架构选型

上一篇拆解了 Kafka 的分区、副本与消费组,你会发现它本质上是一套"追加写日志 + 分区并行 + 副本容错"的系统。RocketMQ 和 Pulsar 都在这个思路上做了延伸,但方向不同:RocketMQ 更贴近电商事务场景,把延迟消息、事务消息做成了原生能力;Pulsar 则把存储和计算拆开,用 BookKeeper 做存储层,主打多租户和分层存储。本篇先讲 Pulsar 独有的消费模型,再给出一套可复用的选型打分方法。

一、Pulsar 的订阅类型:比消费组更细的消费语义

Pulsar 与 Kafka 最大的差异之一是订阅(Subscription)类型。Kafka 只有"消费组"一种负载均衡方式,而 Pulsar 提供四种:exclusive(一个消费者独占)、failover(主备切换)、shared(轮询均分,但会打乱顺序)、key_shared(按 key 哈希,同 key 保序且可并行)。理解 key_shared 与 shared 的区别,就能理解 Pulsar 如何在不牺牲并行的前提下保住顺序。下面模拟这四种订阅如何把消息分给消费者。

defstable_hash(s):returnsum(ord(c)forcins)defassign_shared(messages,consumers):out={c:[]forcinconsumers}fori,(k,v)inenumerate(messages):out[consumers[i%len(consumers)]].append(v)returnoutdefassign_key_shared(messages,consumers):out={c:[]forcinconsumers}fork,vinmessages:out[consumers[stable_hash(k)%len(consumers)]].append(v)returnoutdefassign_exclusive(messages,consumers):return{consumers[0]:[vfor_,vinmessages]}defassign_failover(messages,consumers):out={c:[]forcinconsumers}out[consumers[0]]=[vfor_,vinmessages]returnout messages=[("user:1","a1"),("user:1","a2"),("user:2","a3"),("user:2","a4"),("user:3","a5"),("user:3","a6")]forname,fnin[("exclusive",assign_exclusive),("failover",assign_failover),("shared",assign_shared),("key_shared",assign_key_shared)]:print(f"{name:<10}:",fn(messages,["c1","c2","c3"]))

运行输出:

exclusive : {'c1': ['a1', 'a2', 'a3', 'a4', 'a5', 'a6']} failover : {'c1': ['a1', 'a2', 'a3', 'a4', 'a5', 'a6'], 'c2': [], 'c3': []} shared : {'c1': ['a1', 'a4'], 'c2': ['a2', 'a5'], 'c3': ['a3', 'a6']} key_shared: {'c1': ['a3', 'a4'], 'c2': ['a5', 'a6'], 'c3': ['a1', 'a2']}

看 shared 和 key_shared 的差异:shared 按到达顺序轮流分配,user:1的两条消息 a1、a2 被拆到了 c1 和 c2 两个消费者,顺序无法保证;key_shared 按 key 哈希分配,user:1的 a1、a2 始终落在 c3,同 key 的顺序保住了,同时不同 key 仍然可以并行。这正是 Pulsar 相比 Kafka 消费组更精细的地方——Kafka 要保证顺序只能靠"一个 key 一个分区、一个分区一个消费者",而 key_shared 允许一个分区被多个消费者并行消费,同时每个 key 内部有序。

RocketMQ 的消费模型则更接近 Kafka,靠"队列(Queue)+ 消费组"实现负载均衡,但它把队列数量做成了创建时固定、支持更丰富的顺序消息(严格顺序队列)和延迟消息等级。两者各有侧重,选型时不能只看性能榜单。

二、选型打分:把"感觉"换成可计算的权重

选型失败最常见的根因是照搬别人的结论。正确做法是先列出你的场景真正看重的维度,给每个维度定权重,再给每个产品打分,最后看加权分。权重和分数都是主观的,但一旦显式写出来,团队就能针对"该给 Kafka 的事务功能打 3 分还是 4 分"这类分歧展开讨论,而不是空对空地吵"哪个更好"。下面是一套示例:假设一个既要事务消息、又看重存储成本和云原生的场景。

CRITERIA={"吞吐量":0.20,"消息延迟":0.15,"功能丰富度(事务/延迟/顺序)":0.20,"运维复杂度(越低越好)":0.15,"生态与人才":0.10,"存储成本(分层存储)":0.10,"多租户与云原生":0.10,}SCORES={"Kafka":{"吞吐量":5,"消息延迟":4,"功能丰富度(事务/延迟/顺序)":3,"运维复杂度(越低越好)":3,"生态与人才":5,"存储成本(分层存储)":2,"多租户与云原生":3},"RocketMQ":{"吞吐量":4,"消息延迟":4,"功能丰富度(事务/延迟/顺序)":5,"运维复杂度(越低越好)":3,"生态与人才":3,"存储成本(分层存储)":2,"多租户与云原生":3},"Pulsar":{"吞吐量":4,"消息延迟":4,"功能丰富度(事务/延迟/顺序)":4,"运维复杂度(越低越好)":2,"生态与人才":2,"存储成本(分层存储)":5,"多租户与云原生":5},}defweighted_score(product):returnsum(SCORES[product][c]*wforc,winCRITERIA.items())forpinSCORES:print(f"{p}:{weighted_score(p):.2f}")print("推荐:",max(SCORES,key=weighted_score))

运行输出:

Kafka: 3.65 RocketMQ: 3.65 Pulsar: 3.70 推荐: Pulsar

三者分数非常接近,Pulsar 以微弱优势胜出。这个结果本身不是重点,重点是它把决策过程可视化了:Pulsar 靠"存储成本"和"多租户"两个满分维度拉高了总分,但如果你的团队没有懂 Pulsar/BookKeeper 的人,“运维复杂度"这一项应该打更低的分,Kafka 的"生态与人才"优势就会反超。所以正确的用法是:先改权重、再改分数,把分数和团队真实能力对齐,最后看哪个产品稳定胜出。选型从来不是"客观最好”,而是"在你的约束下代价最小"。

无论选哪个,接下来的五篇都不依赖具体产品:下一篇开始进入所有 MQ 都要面对的共同难题——消息确认、重试与幂等消费,这是保证"至少一次投递"下业务不重复出错的通用方法论。

三、RocketMQ 架构与 Pulsar 存储分离的深层差异

选型打分是表层,真正决定长期成本的是两者架构的根本差异。RocketMQ 沿用"Broker 即存储"的经典模型,靠 NameServer 做无状态的路由注册,Broker 按主从复制,写入走主、从节点异步或同步刷盘。它的优势是事务消息和延迟消息原生、延迟低,适合电商订单、支付这类强业务场景;代价是存储和计算绑定,扩容时要同时考虑两者,历史消息无法廉价地无限保留。

Pulsar 则把 Broker(计算层)和 BookKeeper(存储层)彻底拆开。Broker 无状态,可以随意扩缩容;存储由 BookKeeper 的 bookie 节点承担,数据以 segment 形式分散存放。这个架构带来两个独有能力:一是分层存储(tiered storage),冷消息自动下沉到对象存储,存储成本大幅下降,这也是上一篇打分里 Pulsar"存储成本"拿满分的原因;二是真正的多租户,租户/命名空间/topic 三级隔离,适合平台型公司给多个团队共享一套集群。代价是组件更多(多了 BookKeeper 和 ZooKeeper),运维复杂度最高,团队没有相关经验时,出问题比 Kafka/RocketMQ 更难定位。

一个更实用的选型经验是"人才优先":消息中间件要稳定运行很多年,团队能长期维护比某款产品纸面性能领先 10% 更重要。如果团队已经深度使用 Kafka 生态(如 Kafka Streams、Kafka Connect),迁移到 Pulsar 的隐性成本远高于架构收益;如果是从零开始且明确要分层存储和多租户,Pulsar 才值得纳入。下一篇进入与产品无关的通用难题——消息确认、重试与幂等消费。

落到具体建议:做海量日志、埋点、流处理,选 Kafka,它的生态(Flink、Spark、Kafka Streams)最成熟;做电商交易、支付、订单,选 RocketMQ,事务消息和延迟消息能直接复用;做多租户平台、需要无限保留历史消息,选 Pulsar,分层存储省下的钱最可观。这三句话不是绝对真理,但可以作为选型讨论的起点,再用前面的打分表把结论落实到团队的具体约束上。

选型结果要写进文档并留档,记录当时的权重和分数,因为几个月后团队可能会问"当初为什么选这个"——没有留档的选型,就是下一次换型争议的起点。

参考来源

  • Pulsar:消息与订阅概念
  • Pulsar:架构总览
  • RocketMQ:官方文档

👍 觉得有用就点个赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。

🚀 本文属于《消息队列实战》系列,持续更新,关注不迷路。

📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到cj2664@qq.com,我免费发你。
如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 0:24:04

企业AI落地怎么选?原生开发与低代码开发全维度对比

做企业AI落地这些年&#xff0c;我最大的感受是&#xff1a;越来越多的企业已经不再纠结“要不要上AI”&#xff0c;而是卡在“到底怎么开发”这一步。市面上路径看着多&#xff0c;真正拿到台面上比较的&#xff0c;无非就是原生开发和低代码开发两条路线。原生开发听起来很“…

作者头像 李华
网站建设 2026/9/15 0:23:30

EMC工程实战:干扰源、耦合路径与敏感设备的动态平衡

1. 这不是教科书里的概念搬运&#xff0c;而是工程师在产线凌晨三点调不出EMC问题时的真实抓手“电磁干扰”、“敏感性”、“抗扰度”——这三个词&#xff0c;你可能在产品规格书里扫过一眼&#xff0c;在安规报告里见过几行结论&#xff0c;在EMC实验室门口的告示牌上瞥过一回…

作者头像 李华
网站建设 2026/9/15 0:23:18

Node.js+Express+MySQL+Vue在线电影票购买系统实战

打开网络购票系统那一刻&#xff0c;用户不会关心你后端用了什么框架、数据库设计了几个表、接口是 RESTful 还是 RPC。他只知道——我要在 30 秒内选好座位、下单、看到出票成功。而你作为开发者&#xff0c;最怕的恰恰是 30 秒之后&#xff0c;系统在并发下出现重复卖座、订单…

作者头像 李华
网站建设 2026/9/15 0:21:58

零基础学网络安全:VMware+Kali环境搭建到内网渗透与免杀入门

网络安全这行听起来又酷又难&#xff0c;动不动就是“黑客”“渗透”“攻防”&#xff0c;搞得很多零基础的朋友还没开始就觉得自己不行。但我在这个圈子里摸爬滚打了十来年&#xff0c;最清楚一件事&#xff1a;大部分人学不下去&#xff0c;根本不是什么智商不够、数学不行&a…

作者头像 李华
网站建设 2026/9/15 0:21:42

商业广告追踪技术解析与反欺诈防御实践

1. 商业广告追踪器的技术本质与运作机制商业广告追踪器本质上是一套基于用户行为数据采集与分析的技术体系。其核心技术组件包括&#xff1a;数据采集层&#xff1a;通过JavaScript代码片段、像素标签&#xff08;Pixel Tags&#xff09;和SDK植入等方式&#xff0c;实时捕获用…

作者头像 李华
网站建设 2026/9/15 0:21:10

LangChain SQL查询代理:让自然语言操作数据库成为现实

1. LangChain SQL查询代理项目概述在数据驱动的时代&#xff0c;如何让非技术人员也能轻松查询和分析数据库中的信息&#xff1f;这正是LangChain SQL查询代理要解决的核心问题。这个项目通过结合大语言模型&#xff08;LLM&#xff09;和SQL数据库操作能力&#xff0c;构建了一…

作者头像 李华