这是一段基于Java开发的Kafka消费者代码。在该代码中,我们使用了多线程机制、线程池和多个消费线程来同时消费Kafka中的消息。具体实现步骤如下:
- 在KafkaConsumerExample构造函数中,传入了Kafka主题、Kafka服务器地址、消费者组ID和消费线程数等参数;
- 在consume()方法中,首先配置了Kafka消费者属性,并创建了KafkaConsumer实例;
- 然后订阅Kafka主题,并创建线程池;
- 接下来使用consumer.poll()方法从Kafka中拉取消息;
- 针对每一条消息,创建一个消费线程,通过线程池来处理消费线程;
- 最后,关闭线程池和Kafka消费者。
在该消费者中,消费线程实现了Runnable接口,在其run()方法中实现了消费者实际的逻辑,通过调用ConsumerRecord.value()方法来获取消息。消费者逻辑可以根据具体需求进行实现。
package com.controller;
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;
import java.util.concurrent.*;
public class KafkaConsumerExample {
private final String topic;
private final String bootstrapServers;
private final String groupId;
private final int numThreads;
public KafkaConsumerExample(String topic, String bootstrapServers, String groupId, int numThreads) {
this.topic = topic;
this.bootstrapServers = bootstrapServers;
this.groupId = groupId;
this.numThreads = numThreads;
}
public void consume() {
// 配置 Kafka 消费者属性
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
// 创建 Kafka 消费者实例
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
// 订阅主题
consumer.subscribe(Collections.singleton(topic));
// 创建线程池
// ExecutorService executor = Executors.newFixedThreadPool(numThreads);
// 获取当前机器的 CPU 核心数
int corePoolSize = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
corePoolSize * 2,
10L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
// ThreadPoolExecutor(
// int corePoolSize, // 线程池核心线程数,即初始化时的线程数,达到核心线程数后会将任务放入任务队列中等待处理,默认情况下即使有空闲线程也会将任务加入到队列中
// int maximumPoolSize, // 线程池最大线程数,当任务队列已满并且当前线程数小于 maximumPoolSize 时,会创建新的线程来处理任务
// long keepAliveTime, // 线程池中线程空闲时间的上限,超过这个时间的空闲线程将会被终止,直到线程池中的线程数减少到 corePoolSize 为止
// TimeUnit unit, // keepAliveTime 的时间单位,一般设置为 TimeUnit.SECONDS 或 TimeUnit.MILLISECONDS
// BlockingQueue<Runnable> workQueue, // 任务队列,用于存储等待执行的任务,常见的有 ArrayBlockingQueue、LinkedBlockingQueue 和 SynchronousQueue 等,具体选择根据实际情况决定
// ThreadFactory threadFactory, // 创建线程的工厂,常用的是 Executors.defaultThreadFactory(),也可以自己实现 ThreadFactory 接口来自定义线程的名称、线程组等属性
// RejectedExecutionHandler handler // 当线程池已满,并且等待队列已满,无法接收新任务时的拒绝策略,常见的有 AbortPolicy(抛出 RejectedExecutionException)、CallerRunsPolicy(由调用线程处理)、DiscardOldestPolicy(丢弃等待队列最旧的任务)和 DiscardPolicy(直接丢弃新任务)等
// )
try {
while (true) {
// 从 Kafka 中拉取消息
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
// 创建并执行消费线程
for (ConsumerRecord<String, String> record : records) {
executor.submit(new ConsumerThread(record));
}
}
} finally {
// 关闭线程池和 Kafka 消费者
executor.shutdown();
consumer.close();
}
}
private static class ConsumerThread implements Runnable {
private final ConsumerRecord<String, String> record;
public ConsumerThread(ConsumerRecord<String, String> record) {
this.record = record;
}
@Override
public void run() {
// 消费者业务逻辑
System.out.printf("消费者线程 %s 消费了消息:%s%n", Thread.currentThread().getName(), record.value());
}
}
public static void main(String[] args) {
String topic = "test-topic";
String bootstrapServers = "localhost:9092";
String groupId = "test-group";
int numThreads = 3;
KafkaConsumerExample consumerExample = new KafkaConsumerExample(topic, bootstrapServers, groupId, numThreads);
consumerExample.consume();
}
}