3. 消息队列选型:Kafka vs RabbitMQ vs Pulsar

消息队列选型这事儿,我这些年踩过不少坑。说白了,没有银弹。每个中间件都有自己的脾气,选对了事半功倍,选错了天天加班修数据。

咱们做实时行情数据处理,核心诉求其实就三个:高吞吐、低延迟、不丢数据。嗯,这三个目标有时候是互相矛盾的,得看业务场景做取舍。

3.1 三巨头对比:Kafka、RabbitMQ、Pulsar

先给个直观的对比表,方便你快速建立认知:

特性 Kafka RabbitMQ Pulsar
设计定位 分布式日志流 AMQP消息代理 云原生消息流
吞吐量 百万级/秒 万级/秒 百万级/秒
延迟 毫秒级 微秒级 毫秒级
消息顺序 分区内有序 队列内有序 分区内有序
消息回溯 支持(基于offset) 不支持 支持(基于cursor)
运维复杂度 中等

我个人习惯,先看业务场景再选技术。举个例子:

  • Kafka:适合做数据管道、日志采集、流式计算。我上一家公司的行情系统,每秒要处理50万笔交易,Kafka扛得住。
  • RabbitMQ:适合做任务调度、异步解耦。如果你只是把订单消息从A系统发给B系统,用它最省心。
  • Pulsar:适合做多租户、跨地域复制。我记得有个金融客户,要求上海和香港机房数据实时同步,Pulsar的geo-replication天然支持。

我的建议:实时行情系统,首选Kafka。原因有三:

  1. 行情数据本质是日志流,Kafka的append-only设计完美匹配
  2. 分区机制天然支持并行消费,吞吐量线性扩展
  3. 社区生态成熟,Flink、Spark Streaming都原生集成

3.2 Topic与Partition设计

Topic和Partition是Kafka的核心抽象。很多新手上来就建一个Topic,所有数据往里塞,结果性能一塌糊涂。我刚开始也犯过这个错。

Topic设计原则:按业务实体划分。比如行情系统,我一般这样设计:

topic: stock_trade      # 股票成交数据
topic: stock_quote      # 股票行情快照
topic: stock_order      # 委托单数据
topic: market_index     # 指数行情

为什么这么分?因为不同数据的消费方不同。成交数据要给风控系统,行情快照要给前端展示,分开后可以独立调整分区数和消费策略。

Partition设计:这是性能的关键。我分享一个经验公式:

分区数 = max(生产者吞吐 / 单分区写入能力, 消费者吞吐 / 单分区消费能力)

举个例子,假设你的行情系统每秒要写入100万条消息,单分区能扛5万条/秒,那至少需要20个分区。但别算得太死,留30%的余量。

避坑指南:分区数不是越多越好。我曾经把一个Topic设了100个分区,结果ZooKeeper的选举压力巨大,集群频繁抖动。一般建议单Topic不超过50个分区,除非你用了Kafka 2.8+的KRaft模式。

分区键的选择也很讲究。行情数据我一般用股票代码做分区键,保证同一只股票的数据落在同一个分区里,这样消费端做聚合计算时不用跨分区拉数据。

3.3 消息可靠性保证

做金融数据的,丢消息就是事故。我见过有人因为没配好ACK机制,丢了半小时的行情数据,最后被客户投诉到CEO那里。

Kafka的消息可靠性,主要靠三个参数控制:

参数 取值 含义 可靠性
acks 0 不等待确认 可能丢数据
acks 1 Leader确认即可 Leader宕机可能丢
acks all 所有副本确认 最高可靠性

行情系统我建议用acks=all,配合min.insync.replicas=2。什么意思呢?就是消息必须写入Leader和至少一个Follower才算成功。这样即使Leader挂了,Follower也能顶上。

生产者端还要配重试机制:

# 生产者配置示例
retries = 5
retry.backoff.ms = 100
enable.idempotence = true   # 开启幂等性,防止重复

消费者端同样重要。我习惯手动提交offset:

# 消费者配置
enable.auto.commit = false
# 处理完业务逻辑后再手动提交
consumer.commitSync();

注意:手动提交offset时,一定要在业务处理成功之后再提交。我曾经见过一个同事,先提交offset再处理业务,结果处理到一半系统重启了,消息就永远丢了。嗯,血的教训。

3.4 架构图:消息流转全链路

下面这张图,是我根据实际项目经验画的。它展示了行情数据从采集到消费的完整链路:

实时行情数据消息流转架构 行情数据源 交易所/券商API 生产者 数据采集服务 Kafka 集群 Topic: stock_trade P-0 P-1 P-2 副本因子: 3 | 分区数: 3 acks=all 消费者组A 风控系统 消费者组B 前端展示 消费者组C 数据仓库 可靠性保障机制 • 生产者:acks=all + 幂等性 + 重试机制 • Broker:副本因子=3 + min.insync.replicas=2 • 消费者:手动提交offset + 业务处理成功后提交

这张图里,我特意把可靠性保障机制单独标出来了。你看,从数据源到生产者,从生产者到Kafka,再从Kafka到消费者,每个环节都有对应的可靠性策略。少了任何一个环节,都可能出问题。

3.5 选型总结

说了这么多,最后给个结论性的建议:

  • 小规模系统(日处理百万级消息):RabbitMQ够用,运维简单,延迟低
  • 中等规模系统(日处理亿级消息):Kafka是标配,生态好,坑少
  • 大规模系统(日处理百亿级消息):Pulsar值得考虑,但团队要有足够的技术储备

我个人在实时行情项目里,清一色用的Kafka。不是因为它最好,而是因为它最稳。你想想看,金融系统最怕什么?最怕线上出问题没人能修。Kafka的社区活跃度最高,遇到问题StackOverflow上基本都能找到答案。

嗯,消息队列选型这块,今天就聊到这儿。记住一句话:选技术不是选最潮的,而是选你最熟悉的


专注资料整理