本文目录导读:

我来为你详细解析Kafka Consumer的完整案例,包括基础消费、高级特性和实战场景。
基础消费者案例
最简单的消费者
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.Collections;
import java.util.Properties;
public class SimpleConsumer {
public static void main(String[] args) {
// 1. 配置消费者
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "test-group");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
// 2. 创建消费者
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
// 3. 订阅主题
consumer.subscribe(Collections.singletonList("test-topic"));
try {
// 4. 消费消息
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf("offset = %d, key = %s, value = %s, partition = %d%n",
record.offset(), record.key(), record.value(), record.partition());
}
}
} finally {
consumer.close();
}
}
}
手动提交偏移量
精确控制提交策略
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;
import java.time.Duration;
import java.util.*;
public class ManualCommitConsumer {
public static void main(String[] args) {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "manual-commit-group");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
// 禁止自动提交
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
// 设置批量大小
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 100);
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("test-topic"));
try {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
// 处理所有消息
for (ConsumerRecord<String, String> record : records) {
processRecord(record);
}
// 手动同步提交
consumer.commitSync();
// 或者手动异步提交 with callback
// consumer.commitAsync((offsets, exception) -> {
// if (exception != null) {
// System.err.println("Commit failed for offsets " + offsets);
// }
// });
}
} finally {
try {
// 最后同步提交,确保提交成功
consumer.commitSync();
} finally {
consumer.close();
}
}
}
private static void processRecord(ConsumerRecord<String, String> record) {
// 业务处理逻辑
System.out.println("Processing: " + record.value());
}
}
精准一次性消费模式
结合事务的消费者
import org.apache.kafka.clients.consumer.*;
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.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import java.time.Duration;
import java.util.*;
public class ExactlyOnceConsumer {
private static KafkaProducer<String, String> createProducer() {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "consumer-transaction-1");
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true");
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
producer.initTransactions();
return producer;
}
public static void main(String[] args) {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "exactly-once-group");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
props.put(ConsumerConfig.ISOLATION_LEVEL_CONFIG, "read_committed");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("input-topic"));
KafkaProducer<String, String> producer = createProducer();
try {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
// 开启事务
producer.beginTransaction();
try {
// 处理消息并发送到输出主题
for (ConsumerRecord<String, String> record : records) {
String processedValue = process(record.value());
producer.send(new ProducerRecord<>("output-topic",
record.key(), processedValue));
}
// 提交消费偏移量到事务
Map<TopicPartition, OffsetAndMetadata> offsets = new HashMap<>();
for (ConsumerRecord<String, String> record : records) {
offsets.put(new TopicPartition(record.topic(), record.partition()),
new OffsetAndMetadata(record.offset() + 1));
}
producer.sendOffsetsToTransaction(offsets, "exactly-once-group");
// 提交事务
producer.commitTransaction();
} catch (Exception e) {
// 回滚事务
producer.abortTransaction();
throw e;
}
}
} finally {
consumer.close();
producer.close();
}
}
private static String process(String value) {
// 业务处理
return "processed: " + value;
}
}
多线程消费者案例
使用线程池并发处理
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
public class MultiThreadConsumer {
private static final int THREAD_COUNT = 4;
private static final ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
private static final AtomicLong totalProcessed = new AtomicLong(0);
public static void main(String[] args) {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "multi-thread-group");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500);
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 300000); // 5分钟
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("test-topic"));
try {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
if (!records.isEmpty()) {
// 提交给线程池处理
List<Future<?>> futures = new ArrayList<>();
for (ConsumerRecord<String, String> record : records) {
Future<?> future = executor.submit(() -> {
try {
processRecord(record);
} catch (Exception e) {
e.printStackTrace();
}
});
futures.add(future);
}
// 等待所有任务完成
for (Future<?> future : futures) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
// 同步提交
consumer.commitSync();
System.out.println("Total processed: " + totalProcessed.incrementAndGet());
}
}
} finally {
consumer.close();
executor.shutdown();
}
}
private static void processRecord(ConsumerRecord<String, String> record) {
// 模拟耗时处理
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Thread: " + Thread.currentThread().getName() +
" processing: " + record.value());
}
}
指定分区消费
手动分配分区
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.*;
public class PartitionAssignConsumer {
public static void main(String[] args) {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "partition-group");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
// 手动分配特定分区的特定偏移量
TopicPartition partition0 = new TopicPartition("test-topic", 0);
TopicPartition partition1 = new TopicPartition("test-topic", 1);
consumer.assign(Arrays.asList(partition0, partition1));
// 从最新偏移量开始消费
// consumer.seekToBeginning(Arrays.asList(partition0, partition1));
// 或从指定偏移量开始消费
consumer.seek(partition0, 100); // 从分区0的第100条消息开始
try {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecord<String, String> record : records) {
System.out.printf("Partition: %d, Offset: %d, Key: %s, Value: %s%n",
record.partition(), record.offset(), record.key(), record.value());
}
// 异步提交
consumer.commitAsync((offsets, exception) -> {
if (exception != null) {
System.err.println("Commit failed: " + exception.getMessage());
}
});
}
} finally {
consumer.close();
}
}
}
消费者组动态分配监控
带消费者组信息的客户端
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.*;
public class ConsumerGroupInfoExample {
public static void main(String[] args) {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "monitoring-group");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "5000");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
// 设置消费者组重平衡监听器
consumer.subscribe(Arrays.asList("test-topic"), new ConsumerRebalanceListener() {
@Override
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
System.out.println("Partitions revoked: " + partitions);
// 在重平衡前保存偏移量
consumer.commitSync();
}
@Override
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
System.out.println("Partitions assigned: " + partitions);
// 可以在这里从外部存储恢复偏移量
}
});
try {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
// 获取当前消费者组的分配信息
if (!records.isEmpty()) {
System.out.println("Group info: " + consumer.groupMetadata());
for (ConsumerRecord<String, String> record : records) {
System.out.printf("Received: topic=%s partition=%d offset=%d key=%s value=%s%n",
record.topic(), record.partition(), record.offset(),
record.key(), record.value());
}
}
}
} finally {
consumer.close();
}
}
}
实战:带重试和死信队列的消费者
企业级消息处理模式
import org.apache.kafka.clients.consumer.*;
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.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import java.time.Duration;
import java.util.*;
public class EnterpriseConsumer {
private static final int MAX_RETRIES = 3;
private static final String DLQ_TOPIC = "test-topic-dlq";
public static void main(String[] args) {
Properties consumerProps = new Properties();
consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "enterprise-group");
consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
Properties producerProps = new Properties();
producerProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
producerProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
producerProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(consumerProps);
KafkaProducer<String, String> producer = new KafkaProducer<>(producerProps);
consumer.subscribe(Arrays.asList("test-topic"));
try {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecord<String, String> record : records) {
try {
processWithRetry(record);
} catch (Exception e) {
System.err.println("Processing failed after retries: " + e.getMessage());
// 发送到死信队列
producer.send(new ProducerRecord<>(DLQ_TOPIC, record.key(), record.value()));
producer.flush();
}
}
// 确认所有消息已处理
consumer.commitSync();
}
} finally {
consumer.close();
producer.close();
}
}
private static void processWithRetry(ConsumerRecord<String, String> record) throws Exception {
Exception lastException = null;
for (int retry = 0; retry < MAX_RETRIES; retry++) {
try {
// 模拟业务处理
System.out.println("Attempt " + (retry + 1) + ": processing " + record.value());
processMessage(record.value());
return; // 成功则返回
} catch (Exception e) {
lastException = e;
System.err.println("Attempt " + (retry + 1) + " failed: " + e.getMessage());
// 退避等待
try {
Thread.sleep(1000 * (retry + 1));
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted during retry", ie);
}
}
}
throw lastException;
}
private static void processMessage(String message) throws Exception {
// 模拟可能导致失败的业务逻辑
if (message.contains("error")) {
throw new RuntimeException("Business logic error");
}
System.out.println("Successfully processed: " + message);
}
}
性能优化案例
批量处理和高吞吐配置
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
public class OptimizedConsumer {
public static void main(String[] args) {
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "optimized-group");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
// 性能优化配置
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "1000");
props.put(ConsumerConfig.FETCH_MIN_BYTES_CONFIG, "1024");
props.put(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG, "500");
props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, "1048576");
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
// 禁用消费者组协调
props.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,
"org.apache.kafka.clients.consumer.RoundRobinAssignor");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("high-volume-topic"));
try {
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
if (records.isEmpty()) {
continue;
}
// 批量处理
Map<Integer, List<ConsumerRecord<String, String>>> byPartition =
records.records(new TopicPartition("high-volume-topic", 0));
// 按分区批量处理
records.partitions().forEach(partition -> {
List<ConsumerRecord<String, String>> partitionRecords =
records.records(partition);
// 批量处理逻辑
List<String> batchValues = partitionRecords.stream()
.map(ConsumerRecord::value)
.collect(Collectors.toList());
// 批量保存到数据库等
batchSave(batchValues);
});
// 自动提交由Kafka处理
}
} finally {
consumer.close();
}
}
private static void batchSave(List<String> values) {
// 批量保存到数据库
System.out.println("Batch saving " + values.size() + " records");
}
}
关键配置说明
| 配置项 | 默认值 | 说明 |
|---|---|---|
bootstrap.servers |
Kafka服务器地址 | |
group.id |
消费者组ID | |
enable.auto.commit |
true | 是否自动提交偏移量 |
auto.commit.interval.ms |
5000 | 自动提交间隔 |
auto.offset.reset |
latest | 偏移量重置策略 |
max.poll.records |
500 | 每次poll最大记录数 |
max.poll.interval.ms |
300000 | 最大poll间隔 |
session.timeout.ms |
10000 | 会话超时时间 |
isolation.level |
read_uncommitted | 事务隔离级别 |
- 使用异步提交时:确保最终能提交成功
- 重平衡处理:在重平衡前保存偏移量
- 异常处理:为不可恢复的异常准备DLQ
- 性能优化:合理设置批量大小和拉取间隔
- 监控:记录消费者组的lag等信息
这些案例覆盖了Kafka Consumer的主要使用场景,你可以根据实际需求选择合适的模式,需要我详细解释某个特定场景吗?