选型先问三个问题

消息队列是后端架构里的高速公路,选错了再换代价极大。选型前先问自己三个问题:第一,业务需要多高的吞吐?是每秒几百条还是每秒几十万条;第二,延迟敏感吗?是允许秒级延迟还是要求毫秒级投递;第三,消息语义要多强?能不能容忍丢失,需不需要严格顺序,要不要消息回溯。

这三个答案基本决定了候选范围:吞吐和延迟决定引擎类型,语义决定使用方式。很多团队栽在把"吞吐高的"和"延迟低的"当成同一个东西,其实 Kafka 的高吞吐和 RabbitMQ 的低延迟走的是完全不同的设计路线。

本文核心知识点
  • 吞吐单位时间能处理的消息量,决定横向扩展。
  • 延迟消息从生产到可消费的间隔时间。
  • 语义至少一次、至多一次、恰好一次的投递保证。
  • 模型队列模型与发布订阅模型的差异。
消息队列选型配图:蓝紫渐变背景上方半透明圆盘悬浮,下方三个并排圆角矩形管道模块各接入一座高低不同的纯色方块塔,扁平科技插画
不同消息队列就像不同规格的管道与储池,选型就是匹配业务需求的尺码。

三种消息队列的定位

RabbitMQ 是最老牌的消息中间件之一,基于 AMQP 协议,以交换器加队列的灵活路由著称,单机吞吐在每秒几万条量级,胜在功能完整、生态成熟、运维友好,适合企业内部系统集成与复杂路由场景。

Kafka 是分布式日志流的代名词,以追加日志和消费者组为核心,单集群吞吐轻松到每秒百万条,天然支持消息回溯与多消费者,适合大数据管道、日志采集、事件流与削峰填谷。Pulsar 则是后起之秀,把存储和计算分离,用 BookKeeper 做分层存储,支持多租户与秒级扩容,吞吐与 Kafka 相当,同时在消息模型上更贴近 RabbitMQ 的灵活路由。

# 一句话概括三者的典型定位
RabbitMQ = 灵活路由 + 可靠投递(企业集成)
Kafka    = 高吞吐 + 日志回放(数据管道)
Pulsar   = 多租户 + 存储计算分离(云原生)

吞吐与延迟对比

吞吐的核心在磁盘与网络的使用方式。RabbitMQ 每条消息都要确认并落盘,为可靠性牺牲了批量能力,延迟通常在毫秒到十毫秒级,但吞吐上到万级后需要靠集群分摊。Kafka 采用顺序写磁盘加批量发送,把随机 IO 变成顺序 IO,单分区即可支撑较高吞吐,代价是端到端延迟通常十毫秒以上,追求低延迟反而要控制批量大小。

Pulsar 的存储层独立成 BookKeeper,写入先落 journal 再异步刷盘,兼顾吞吐与延迟,分段存储让扩容不搬数据。选择时记住:要极低延迟且吞吐中等,选 RabbitMQ;要极高吞吐且能接受一定延迟,选 Kafka;要云原生多租户、吞吐与延迟都要,选 Pulsar。

# Kafka 生产端关键参数(吞吐优先)
batch.size=16384          # 批次越大吞吐越高
linger.ms=5               # 攒批等待时长
compression.type=lz4      # 压缩减小网络传输
acks=all                  # 副本确认保证不丢

消息模型与消费语义

RabbitMQ 是队列模型加交换器路由:生产者把消息交给交换器,交换器按绑定规则投到队列,每个队列的消息只会被一个消费者消费,多个消费者按轮询或公平分发竞争同一队列。Kafka 是分区日志模型:主题分成多个分区,每个分区的消息按序追加,同一消费组内的消费者各领分区,一个分区同时只给一个消费者。

消费语义三档
  • 至少一次可能重复,业务必须幂等。
  • 至多一次可能丢失,适合可容忍丢数据的场景。
  • 恰好一次事务加幂等,成本最高。
# Kafka 消费者:至少一次语义的标配
props.setProperty("enable.auto.commit", "false");
// 业务处理成功后手动提交偏移量
consumer.commitSync();

做选型时把消息语义当作契约写进设计文档:允许重复就降低实现成本,要求恰好一次就要评估事务开销,Kafka 的幂等生产者和事务支持在吞吐与强语义之间给了折中。

高可用与运维成本

RabbitMQ 用镜像队列做高可用,主从复制粒度是队列,节点数翻倍时队列都要复制,容量增长不明显,运维上依赖 Erlang 节点互联,集群规模通常控制在几台。Kafka 的高可用靠分区副本,副本分布在不同 broker,ISR 机制保证主副本故障时从 ISR 里选新主,扩容就是加节点并迁移分区,运维体系成熟但概念多。

Pulsar 的 broker 无状态,扩容直接加 broker,存储交给 BookKeeper 自动均衡,多租户的 quota 与隔离开箱即用,运维最省心但组件更多,小团队上手门槛更高。运维成本往往是被忽略的选型变量:一个没人会运维的队列,再好的性能也撑不起生产。

选型决策与迁移

决策可以按场景矩阵收敛:业务系统集成、延迟敏感、路由复杂选 RabbitMQ;日志、埋点、事件流、大数据管道选 Kafka;多团队共享、多租户隔离、存储要长时间保留选 Pulsar。规模不大时不要为了"先进"选 Pulsar,也不要为了"省事"让 Kafka 硬扛所有场景。

迁移旧队列有三条路:双写过渡,新旧并行写一段时间再切读;消息桥接,用一个消费转发服务把旧队列消息搬到新队列;流量灰度,按消息类型逐步迁移。迁移期间幂等与顺序性最容易出问题,建议先迁移允许重复消费的流量,最后迁移强顺序的。

# 双写伪代码示意
def send(msg):
    rabbit.send(msg)   # 旧队列继续写
    kafka.send(msg)    # 新队列同步写
    # 观察稳定后关闭 rabbit.send