4、消息队列核心:消息队列模型(点对点、发布订阅)、RabbitMQ 与 Kafka 延迟对比、磁盘顺序写与页缓存机制

4.1 两种消息模型:点对点 vs 发布订阅

消息队列的模型,说白了就两种:点对点和发布订阅。我刚开始接触消息中间件时,总觉得这两者差不多,直到有一次在项目里用错了模型,导致消息被多个消费者重复消费,数据全乱了。嗯,从那以后我再也不敢混淆它们了。

点对点模型(P2P)

点对点模型,也叫队列模型。一条消息只能被一个消费者消费。消息生产者把消息发到队列里,消费者从队列里拉取。谁抢到算谁的。

我在项目中遇到过这样的场景:订单系统需要异步处理库存扣减。多个库存服务实例都监听同一个队列,但一条订单消息只需要被一个实例处理。这时候点对点模型就非常合适。

核心特点:
  • 一条消息只能被一个消费者消费
  • 消息一旦被消费,就从队列中移除
  • 适合任务分发、负载均衡场景

发布订阅模型(Pub/Sub)

发布订阅模型就完全不一样了。生产者把消息发到主题(Topic)上,所有订阅了这个主题的消费者都能收到这条消息。你想想看,这不就是广播吗?

我记得有一次做实时数据同步,需要把用户行为数据同时推送给推荐系统、监控系统和日志系统。如果用点对点模型,我得复制三份消息分别发到三个队列。用发布订阅模型就简单了,一条消息发到主题,三个订阅者各自消费。

我的选择建议:
  • 任务分发、负载均衡 → 点对点
  • 事件广播、数据同步 → 发布订阅
  • 如果拿不准,先问自己:这条消息需要被多个消费者处理吗?

4.2 RabbitMQ 与 Kafka 延迟对比

说到延迟,很多人一上来就问:RabbitMQ 和 Kafka 哪个延迟更低?其实这个问题没那么简单。我做过不少压测,咱们直接看数据。

维度 RabbitMQ Kafka
端到端延迟(P99) 微秒级(10-100μs) 毫秒级(1-10ms)
吞吐量 万级/秒 百万级/秒
消息确认机制 逐条确认 批量确认
存储方式 内存+磁盘 磁盘顺序写

为什么会这样?说白了就是设计哲学不同。

RabbitMQ 追求的是低延迟和可靠性。每条消息都要确认,确认完才处理下一条。这种设计让延迟非常稳定,但吞吐量上不去。我在金融交易系统里用过 RabbitMQ,延迟基本控制在 50μs 以内,但单机吞吐量也就几万条每秒。

Kafka 追求的是高吞吐。它把消息攒一批再批量写入磁盘,用顺序写的方式把磁盘性能发挥到极致。代价就是延迟会高一些,毕竟要等一批消息凑够才处理。我曾经在日志采集系统里用 Kafka,单机吞吐量轻松跑到 50 万条/秒,但延迟在 2-5ms 左右。

避坑指南: 我曾经犯过一个错误:在实时风控系统里用了 Kafka。风控要求延迟在 1ms 以内,Kafka 的批量处理机制导致延迟波动很大。后来换成了 RabbitMQ,问题才解决。选型时一定要搞清楚你的延迟要求是「平均延迟」还是「P99延迟」。

4.3 磁盘顺序写与页缓存机制

Kafka 为什么能那么快?核心秘密就两个:磁盘顺序写和页缓存。

磁盘顺序写

传统观念里,磁盘很慢。但那是随机读写。顺序写完全是另一回事。我做过测试,机械硬盘的顺序写速度能达到 150MB/s,而随机写只有 1MB/s 左右。差了 150 倍!

Kafka 充分利用了这个特性。它把消息追加到文件末尾,不做任何随机写入。你想想看,这就像往笔记本后面一页页加内容,而不是翻来翻去到处写。

顺序写的优势:
  • 磁盘寻道时间几乎为零
  • 充分利用磁盘带宽
  • 减少磁盘碎片
  • 配合页缓存,性能接近内存

页缓存机制

Kafka 还有一个骚操作:它不自己做缓存,而是直接利用操作系统的页缓存(Page Cache)。

消息写入时,先写到页缓存,然后由操作系统异步刷盘。消息读取时,如果数据还在页缓存里,直接从内存返回,根本不用走磁盘。这招高明在哪?

  • 省去了 JVM 堆内缓存的管理开销
  • 避免了 GC 带来的停顿
  • 操作系统比应用更懂内存管理

我记得有一次排查 Kafka 性能问题,发现消费者消费速度突然变慢。一看监控,页缓存命中率从 99% 降到了 60%。原来是另一台机器在大量读写文件,把页缓存给挤占了。嗯,这就是共享页缓存的副作用。

我的调优经验:
  • 给 Kafka 留足内存,页缓存越大越好
  • 不要和 Kafka 共用机器跑其他 IO 密集的应用
  • 监控 /proc/meminfo 中的 Cached 和 Dirty 指标
  • 如果页缓存命中率低于 90%,考虑增加内存或减少分区数

4.4 核心逻辑关系图

下面这张图展示了消息队列的核心知识体系,我画了很久才理清楚它们之间的关系。

消息队列核心知识体系 消息模型 点对点 (P2P) 一条消息一个消费者 发布订阅 (Pub/Sub) 一条消息多个消费者 任务分发 → P2P 事件广播 → Pub/Sub 延迟对比 RabbitMQ 10-100μs Kafka 1-10ms 低延迟选 RabbitMQ 高吞吐选 Kafka 磁盘顺序写 顺序写速度 ≈ 150MB/s(机械盘) 随机写速度 ≈ 1MB/s Kafka 追加写入,避免随机寻道 充分利用磁盘带宽 页缓存机制 写入 → 页缓存 → 异步刷盘 读取 → 页缓存命中 → 内存返回 省去 JVM 堆内缓存管理 避免 GC 停顿

这张图把消息队列的核心分成了三块:消息模型决定了消息怎么分发,延迟对比帮你做技术选型,磁盘顺序写和页缓存则是 Kafka 高性能的底层支撑。三者环环相扣,缺一不可。

一句话总结: 消息模型解决「怎么发」的问题,延迟对比解决「选哪个」的问题,磁盘顺序写和页缓存解决「怎么快」的问题。搞懂这三块,消息队列的核心你就拿下了。

公众号:蓝海资料掘金营,微信deep3321