为什么Kafka这么快?新手必知的分布式消息系统原理
9

1. 为什么会有Kafka

Kafka诞生的背景:Kafka的诞生源于传统消息系统在​​海量实时数据场景下的根本性瓶颈​​,其设计解决了现代数据架构中的核心痛点。

传统消息队列(如RabbitMQ)在中小规模场景下表现良好,但面对​​每秒百万级消息处理需求​​时,其吞吐量和延迟成为瓶颈。Kafka通过三大创新实现质变:

  1. ​分布式分区架构​​:数据按主题(Topic)划分为多个分区(Partition),支持并行读写,轻松横向扩展。

  2. ​顺序写磁盘+零拷贝技术​​:通过批量写入和磁盘顺序访问,将磁盘I/O从性能劣势转为优势,实现​​毫秒级延迟​​与​​TB级数据吞吐​​(如双十一每秒处理200万订单)。

  3. ​批量压缩传输​​:减少网络带宽占用,提升数据传输效率。

✅ ​​结果​​:相同硬件资源下,Kafka的吞吐量可达传统方案的3倍以上

2. Kafka是什么

2.1. Kafka定义:

Kafka 是一款​​分布式流数据平台​​,最初由 LinkedIn 开发(现属 Apache 基金会开源项目)。它不仅是高性能消息队列,更是现代数据架构的核心基础设施,解决了海量实时数据的​​传输、存储、处理​​三大核心问题。

2.2. Kafka 术语

  • 消息:Kafka 的数据单元被称为消息。消息由字节数组组成。

  • 批次:批次就是一组消息,这些消息属于同一个主题和分区。

  • 主题(Topic):Kafka 消息通过主题进行分类。主题就类似数据库的表。

    • 不同主题的消息是物理隔离的;

    • 同一个主题的消息保存在一个或多个 Broker 上。但用户只需指定消息的 Topic 即可生产或消费数据而不必关心数据存于何处。

    • 主题有一个或多个分区。

  • 分区(Partition):分区是一个有序不变的消息序列,消息以追加的方式写入分区,然后以先入先出的顺序读取。Kafka 通过分区来实现数据冗余和伸缩性。

  • 消息偏移量(Offset):表示分区中每条消息的位置信息,是一个单调递增且不变的值。

  • 生产者(Producer):生产者是向主题发布新消息的 Kafka 客户端。生产者可以将数据发布到所选择的主题中。生产者负责将记录分配到主题中的哪一个分区中。

  • 消费者(Consumer):消费者是从主题订阅新消息的 Kafka 客户端。消费者通过检查消息的偏移量来区分消息是否已读。

  • 消费者群组(Consumer Group):多个消费者共同构成的一个群组,同时消费多个分区以实现高并发。

    • 每个消费者属于一个特定的消费者群组(可以为每个消费者指定消费者群组,若不指定,则属于默认的群组)。

    • 群组中,一个消费者可以消费多个分区

    • 群组中,每个分区只能被指定给一个消费

  • 再均衡(Rebalance):消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。分区再均衡是 Kafka 消费者端实现高可用的重要手段。

  • Broker - 一个独立的 Kafka 服务器被称为 Broker。Broker 接受来自生产者的消息,为消息设置偏移量,并提交消息到磁盘保存;消费者向 Broker 请求消息,Broker 负责返回已提交的消息。

  • 副本(Replica):Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余,这些地方就是所谓的副本。副本还分为领导者副本和追随者副本,各自有不同的角色划分。副本是在分区层级下的,即每个分区可配置多个副本实现高可用。

3. Kafka Java 客户端使用入门

3.1. 引入 maven 依赖

Stream API 的 maven 依赖:

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-streams</artifactId>
    <version>1.1.0</version>
</dependency>

其他 API 的 maven 依赖:

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>1.1.0</version>
</dependency>

3.2. 生产者代码

代码如下,直接通过 send 方法来发送

Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

try (Producer<String, String> producer = new KafkaProducer<>(props)) {
    producer.send(new ProducerRecord<>("test-topic", "key1", "Hello from Java!"));
    System.out.println("消息发送成功");
}

3.3. 消费者代码

消费流程

具体步骤如下:

  1. 创建消费者。

  2. 订阅主题。除了订阅主题方式外还有使用指定分组的模式,但是常用方式都是订阅主题方式

  3. 轮询消息。通过 poll 方法轮询。

  4. 关闭消费者。在不用消费者之后,会执行 close 操作。close 操作会关闭 socket,并触发当前消费者群组的再均衡。

Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "test-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

// 1.构建KafkaCustomer
try (Consumer<String, String> consumer = new KafkaConsumer<>(props)) {
    consumer.subscribe(Collections.singletonList("test-topic"));
    while (true) {
        // 2.设置主题
        ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
        // 3.接受消息
        for (ConsumerRecord<String, String> record : records) {
            System.out.printf("收到消息: offset=%d, key=%s, value=%s%n", 
                record.offset(), record.key(), record.value());
        }
    }
} finally {
        // 4.关闭消息
        consumer.close();
}

4.Kafka吞吐量为什么这么大

RocketMq是起源于Kafka的,但是它的吞吐量为什么没有Kafka这么大?

Kafka 之所以能实现远超传统消息队列的吞吐量(如单机百万级 TPS),核心在于其​​颠覆性的架构设计​​和​​极致的性能优化​​。以下是其高性能背后的关键设计哲学与技术实现:

1、磁盘顺序写入:化劣势为优势​

顺序追加写入​

  • 所有消息以​​追加(Append-Only)​​方式写入磁盘,避免随机寻址

  • 实测:顺序写磁盘速度 ≈ ​​600MB/s​​(机械硬盘),接近内存写入速度

  • 对比:随机写磁盘速度仅 ≈ 100KB/s

2、零拷贝(Zero-Copy)技术:绕过CPU瓶颈​(核心、根本)

传统数据流转路径:磁盘 --> 内核缓冲区 --> 用户缓冲区 --> Socket缓冲区 --> 网卡

需要进行四次拷贝

RocketMq的零拷贝路径:磁盘 --mmap-->内核缓冲区 --Socket缓冲区--> 网卡

需要进行两次拷贝和一次mmap文件映射

Kafka 的零拷贝路径:磁盘 -->内核缓冲区 --> sendfile--> 网卡

步骤​​

RocketMQ (mmap + write)

Kafka (sendfile + DMA Gather)

​1. 磁盘 → 内核缓冲区​

DMA 拷贝

DMA 拷贝

​2. 内核 → 用户态​

​可选​​(仅需解析消息内容时触发)

​无​​(完全绕过用户态)

​3. 内核 → Socket​

CPU 拷贝(write 系统调用)

DMA 拷贝(DMA Gather

​4. Socket → 网卡​

DMA 拷贝

(已合并到第 3 步)

​总拷贝次数​

​3 次(2 DMA + 1 CPU)​

​2 次(纯 DMA)​

只需要两次拷贝

之所以RocketMq要多一次mmap,是因为mmap会返回读取的文件对象,而sendfile只返回文件字节数,RocketMQ需要文件对象去支持高级功能

  • 省去步骤​​:跳过用户态数据拷贝(CPU 消耗降低 50%+)

  • ​技术实现​​:

    • Linux 的 sendfile() 系统调用

    • 配合 DMA(直接内存访问)技术

📌 ​​案例​​:10GB 文件传输,零拷贝比传统方式快 ​​3倍​​(实测延迟从 20ms → 6ms)

如果有10个消费者,传统方式下,数据复制次数为4*10=40次,

而使用“零拷贝技术”只需要1+10=11次,一次为从磁盘复制到页面缓存,

10次表示10个消费者各自读取一次页面缓存。

5.总结

​​Kafka 是一个分布式、高吞吐、低延迟的流数据平台,通过顺序写磁盘、零拷贝和分区并行化实现海量实时数据传输与持久化存储。​

为什么Kafka这么快?新手必知的分布式消息系统原理
https://www.orioncoder.cn/archives/BaWxy8Sw
作者
Orion
发布于
更新于
许可