: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,keyweighted_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 的完整工程 配套部署脚本 / 踩坑清单评论一声或发邮件到cj2664qq.com我免费发你。如果你正好在做类似系统、或有工程化难题想找人做也欢迎邮件聊一句——我按实际情况评估能落地的就接单或出方案。评论和邮件都能直接找到我不用跳别的平台。