什么是 Apache Kafka?
Apache Kafka 是一个开源的分布式流处理平台,旨在处理大量的数据流。它最初由 LinkedIn 开发,后成为 Apache 项目。Kafka 可以用作消息队列,它支持高吞吐量、分布式存储、持久化和实时流处理。Kafka 常用于日志聚合、数据管道、流数据处理等场景。
在本文中,我们将学习如何在 Java 应用中使用 Kafka 进行消息的生产和消费。
1. 环境准备
首先,我们需要设置 Apache Kafka 和 Zookeeper。Kafka 需要 Zookeeper 来协调集群节点的工作。你可以在本地安装 Kafka 和 Zookeeper,或者使用 Docker 来快速启动它们。
1.1 使用 Docker 启动 Kafka 和 Zookeeper
如果你已经安装了 Docker,可以使用 Docker 来快速启动 Kafka 和 Zookeeper。你可以参考以下 docker-compose.yml
文件来启动 Kafka 和 Zookeeper。
version: '2'
services:
zookeeper:
image: wurstmeister/zookeeper:3.4.6
ports:
- "2181:2181"
expose:
- "2181"
kafka:
image: wurstmeister/kafka:latest
expose:
- "9093"
ports:
- "9093:9093"
environment:
KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:9093
KAFKA_LISTENER_SECURITY_PROTOCOL: PLAINTEXT
KAFKA_LISTENER_NAME_INSIDE: INSIDE
KAFKA_LISTENER_PORT_INSIDE: 9093
KAFKA_LISTENER_INTER_BROKER: INSIDE
KAFKA_LISTENER_NAMES: INSIDE
KAFKA_LISTENER_INTERNAL: INSIDE
KAFKA_LISTENER_INTERNAL_PORT: 9093
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
depends_on:
- zookeeper
在 docker-compose.yml
文件中,我们设置了 Zookeeper 和 Kafka 服务。通过此配置,我们可以在本地使用 Kafka 和 Zookeeper。
启动服务:
docker-compose up -d
启动后,Kafka 会运行在 localhost:9093
,而 Zookeeper 会运行在 localhost:2181
。
2. 在 Java 中配置 Kafka 客户端
要在 Java 中使用 Kafka,你需要添加 Kafka 的 Java 客户端依赖。你可以通过 Maven 来管理依赖。
2.1 添加 Maven 依赖
在 pom.xml
中添加 Kafka 依赖:
<dependencies>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>3.3.0</version> <!-- 使用合适的版本 -->
</dependency>
</dependencies>
2.2 创建 Kafka 生产者(Producer)
Kafka 的生产者是负责发送消息到 Kafka 集群中的客户端。你可以通过 KafkaProducer 类来实现生产者。
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class KafkaProducerExample {
public static void main(String[] args) {
// 配置 Kafka 生产者
Properties properties = new Properties();
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9093"); // Kafka 集群地址
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); // 序列化器
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); // 序列化器
// 创建 Kafka 生产者
KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
// 创建一条消息并发送到 Kafka
String topic = "test-topic";
String key = "key1";
String value = "Hello, Kafka!";
ProducerRecord<String, String> record = new ProducerRecord<>(topic, key, value);
try {
producer.send(record, (metadata, exception) -> {
if (exception != null) {
exception.printStackTrace();
} else {
System.out.println("Message sent to topic: " + metadata.topic() + " with offset: " + metadata.offset());
}
});
} catch (Exception e) {
e.printStackTrace();
} finally {
producer.close();
}
}
}
解释:
ProducerConfig.BOOTSTRAP_SERVERS_CONFIG
:指定 Kafka 集群的地址。ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG
和ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG
:配置消息的序列化方式。在这个例子中,我们使用的是StringSerializer
。KafkaProducer.send()
:异步发送消息。你可以通过回调函数获取发送结果。
2.3 创建 Kafka 消费者(Consumer)
Kafka 的消费者是从 Kafka 集群中读取消息的客户端。你可以通过 KafkaConsumer
类来实现消费者。
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.util.Collections;
import java.util.Properties;
public class KafkaConsumerExample {
public static void main(String[] args) {
// 配置 Kafka 消费者
Properties properties = new Properties();
properties.put("bootstrap.servers", "localhost:9093"); // Kafka 集群地址
properties.put("group.id", "test-group"); // 消费者组 ID
properties.put("key.deserializer", StringDeserializer.class.getName()); // 消息键的反序列化器
properties.put("value.deserializer", StringDeserializer.class.getName()); // 消息值的反序列化器
properties.put("auto.offset.reset", "earliest"); // 设置自动偏移量重置策略
// 创建 Kafka 消费者
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties);
// 订阅主题
consumer.subscribe(Collections.singletonList("test-topic"));
// 持续拉取消息
while (true) {
consumer.poll(100).forEach(record -> {
System.out.printf("Consumed message: key=%s, value=%s, partition=%d, offset=%d%n",
record.key(), record.value(), record.partition(), record.offset());
});
}
}
}