字节面经:kafka对比rocketmq,可靠性怎么保证?

89 阅读5分钟

Kafka vs RocketMQ:谁的消息更靠谱?💣

“Kafka 挂了还能保证不丢消息吗?” “RocketMQ 说自己是金融级可靠,那 Kafka 是‘业余级’吗?”

这问题,在后端圈子里堪称灵魂拷问。 很多人第一次接触消息队列时都在纠结:「到底选 Kafka 还是 RocketMQ?哪个更稳?哪个更不容易掉链子?」

别急,这篇文章我们不整那些「书面体总结」,我带你**「像侦探🕵‍♂一样,从消息丢失的每个环节摸清它们的“作案动机”」**。 最后你会发现 —— Kafka 和 RocketMQ 的可靠性,其实是两种哲学。


一、为什么消息会“不靠谱”?🧐

要理解可靠性,先得知道它是怎么“不可靠”的。 消息丢失,主要有三种情况👇

  1. 「生产者没发出去」(网络闪断 / 异常中断)
  2. 「Broker 收了但没写盘」(还没落地就挂了)
  3. 「消费者没消费完」(还没提交 offset 就崩了)

每一步都可能“翻车”,而 Kafka 和 RocketMQ 都有自己的补救措施。 我们先拆开看。


二、生产者端的可靠性:三次确认机制 🚀

🧠 Kafka:Acks 的哲学

Kafka 的生产者可以设置 acks 参数,控制消息确认策略:

acks含义可靠性性能
0不等待 broker 确认❌ 最不可靠✅ 最高
1Leader 写入确认即可⚠️ 一般⚖️ 平衡
all(-1)所有副本确认✅ 最可靠❌ 最慢

Kafka 是一种“你要我稳,就得忍慢”的哲学。 很多公司(比如日志类系统)会选 acks=1,毕竟**「日志丢一两条不心疼,延迟才是命」**。

Java 示例👇

Properties props = new Properties();
props.put("acks", "all");  // ✅ 所有副本确认
props.put("retries", 3);
props.put("enable.idempotence", true); // ✅ 避免重复发送
Producer<String, String> producer = new KafkaProducer<>(props);
producer.send(new ProducerRecord<>("test", "hello kafka"));

Go 版本👇

w := &kafka.Writer{
    Addr:     kafka.TCP("localhost:9092"),
    Topic:    "test",
    Balancer: &kafka.LeastBytes{},
    RequiredAcks: kafka.RequireAll, // ✅ 等待所有副本确认
}
err := w.WriteMessages(context.Background(),
    kafka.Message{Value: []byte("hello kafka")},
)

🧠 RocketMQ:同步刷盘 + 同步复制

RocketMQ 的可靠性策略比 Kafka 更“细腻”: 它分为 「同步/异步刷盘」「同步/异步复制」 两层策略。

模式写盘方式副本同步可靠性延迟
SYNC_MASTER同步写盘✅ 高⚖️ 中等
ASYNC_MASTER异步写盘⚠️ 中✅ 快
SYNC_REPLICATE同步写盘 + 同步复制✅ 超高❌ 最慢

也就是说,RocketMQ 不仅管写盘,还管复制确认。 「如果你做金融支付、订单系统这种容不得掉单的业务,RocketMQ 会更靠谱。」


三、Broker 端:存储可靠性的灵魂之处💾

Kafka:Segment 文件 + Page Cache

Kafka 的消息是**「顺序写入 segment 文件」**的,利用 OS 的 Page Cache 写盘,极快。 但它默认不是实时刷盘(flush.messages=0 表示按时间批量刷)。

Kafka 偏“高吞吐+最终一致”,比如👇

你丢几条日志?我装作没看见。 你要高可靠?可以,加副本。

Kafka 通过 「副本(replication)机制」 保证 broker 挂了也不丢:

  • Leader 写入后通知 Follower
  • 所有 ISR(in-sync replica)确认后才算提交成功

RocketMQ:CommitLog + ConsumeQueue

RocketMQ 的底层设计像是“文件系统工程师的浪漫”:

  • 所有消息先顺序写入 「CommitLog」
  • 消费队列 「ConsumeQueue」 只存索引,快速定位
  • 定时 flush 到磁盘,可配置同步或异步

RocketMQ 在 Broker 层支持更细粒度的容灾,比如:

  • 异地双活(同步双写)
  • 消息重试 + 死信队列(DLQ)
  • Transaction 消息保证业务一致性

这让它成为了“银行系统的老朋友”。


四、消费者端:到底谁不“偷懒”?🧩

Kafka

Kafka 的消费者需要手动提交 offset。 如果你在处理逻辑前提交,就可能丢消息; 如果处理后提交失败,就可能重复消费。

所以最稳的方式是:「业务处理完再 commit offset」

try {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        process(record.value()); // ✅ 业务逻辑
    }
    consumer.commitSync(); // ✅ 确认提交
} catch (Exception e) {
    // ❌ 不提交 -> 下次重试
}

RocketMQ

RocketMQ 消费者更智能。它的消费模型支持:

  • 「At least once(至少一次)」:消息可能重复,但不丢。
  • 「Exactly once(幂等消费)」:业务自行去重。

并且支持**「自动重试」**机制(重试 N 次后进 DLQ)。

Go 版示例👇

c, _ := rocketmq.NewPushConsumer(
    consumer.WithGroupName("testGroup"),
    consumer.WithNsResolver(primitive.NewPassthroughResolver([]string{"localhost:9876"})),
)
c.Subscribe("test", consumer.MessageSelector{}, func(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
    for _, msg := range msgs {
        fmt.Println("Received:", string(msg.Body))
    }
    return consumer.ConsumeSuccess, nil
})

五、延伸思考:可靠 ≠ 万能 ⚡

可靠性不是银弹。 再高的可靠机制,也扛不住“错误的姿势”。 👉 如果你:

  • 没开幂等(Kafka)
  • 没启用同步刷盘(RocketMQ)
  • 消费端乱提交 offset

那再强的 MQ 也白搭。

有句话很扎心但很真:

“消息中间件的可靠性,最后都死在开发者的偷懒上。”


六、实战建议 ✅

场景推荐原因
日志收集 / 实时分析Kafka吞吐高、生态成熟
订单 / 支付系统RocketMQ金融级可靠性
跨机房容灾 / 事务消息RocketMQ原生支持
高并发数据流 / 大数据平台Kafka社区生态强大

七、面试金句总结

  1. 「"Kafka的可靠性是配置出来的,RocketMQ的可靠性是设计出来的"」 🎯
  2. 「"没有绝对可靠的系统,只有适合业务的可靠性方案"」
  3. 「"高可靠性的代价就是性能损失,这是永恒的权衡"」 ⚖️
  4. 「"消息队列的可靠性是一个端到端的问题,光配置Broker是远远不够的"」

八、避坑指南

  1. 「Kafka陷阱」

    • 设置了acks=all但没监控ISR数量
    • 自动提交offset导致消息丢失
    • 分区数过多影响可靠性
  2. 「RocketMQ陷阱」

    • 误用异步刷盘+异步复制组合
    • 死信队列没有监控和处理
    • NameServer集群配置不当

✨结尾:两种可靠性的哲学

Kafka 的哲学是:「我不保证你永远安全,但我让你跑得快」。 RocketMQ 的哲学是:「你要多稳都行,但得付出代价」

这就像你选伴侣:

Kafka 是那个爱冒险的“程序员恋人”,RocketMQ 是那个稳重靠谱的“会计师朋友”。😏

别纠结谁更强,关键看你要的是什么。


如果这篇文章让你更清楚 Kafka 和 RocketMQ 的区别, 别忘了点个「在看」👍 让更多后端兄弟别再被“可靠性”这两个字整懵圈。