1. 为什么会有Kafka
Kafka诞生的背景:Kafka的诞生源于传统消息系统在海量实时数据场景下的根本性瓶颈,其设计解决了现代数据架构中的核心痛点。
传统消息队列(如RabbitMQ)在中小规模场景下表现良好,但面对每秒百万级消息处理需求时,其吞吐量和延迟成为瓶颈。Kafka通过三大创新实现质变:
分布式分区架构:数据按主题(Topic)划分为多个分区(Partition),支持并行读写,轻松横向扩展。
顺序写磁盘+零拷贝技术:通过批量写入和磁盘顺序访问,将磁盘I/O从性能劣势转为优势,实现毫秒级延迟与TB级数据吞吐(如双十一每秒处理200万订单)。
批量压缩传输:减少网络带宽占用,提升数据传输效率。
✅ 结果:相同硬件资源下,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. 消费者代码
消费流程
具体步骤如下:
创建消费者。
订阅主题。除了订阅主题方式外还有使用指定分组的模式,但是常用方式都是订阅主题方式
轮询消息。通过 poll 方法轮询。
关闭消费者。在不用消费者之后,会执行 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,是因为mmap会返回读取的文件对象,而sendfile只返回文件字节数,RocketMQ需要文件对象去支持高级功能
省去步骤:跳过用户态数据拷贝(CPU 消耗降低 50%+)
技术实现:
Linux 的
sendfile()
系统调用配合 DMA(直接内存访问)技术
📌 案例:10GB 文件传输,零拷贝比传统方式快 3倍(实测延迟从 20ms → 6ms)
如果有10个消费者,传统方式下,数据复制次数为4*10=40次,
而使用“零拷贝技术”只需要1+10=11次,一次为从磁盘复制到页面缓存,
10次表示10个消费者各自读取一次页面缓存。
5.总结
Kafka 是一个分布式、高吞吐、低延迟的流数据平台,通过顺序写磁盘、零拷贝和分区并行化实现海量实时数据传输与持久化存储。