Java实现Kafka消费者(Consumer)两种方式

实现在Spring Boot项目中监听Kafka指定topic中的消息,有两种实现思路:

一种是使用Spring Boot提供的@KafkaListener注解

另外一种是在kafka提供的原生java客户端中,消费者使用定时任务或者采while(true){…}进行消息拉取,这种方式可以避免与parent 版本出现冲突

目录

一、@KafkaListener注解

二、while(true){…}用.poll()方式进行消息拉取


一、@KafkaListener注解

导入依赖

        <!-- spring-kafka -->
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <version>版本号</version>
        </dependency>

配置文件

# kafka地址
spring.kafka.bootstrap-servers=127.0.0.1:9092
# 消费者组ID
spring.kafka.consumer.group-id=1
# 键序列化方式
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
# 值序列化方式
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
# 默认提交偏移量
spring.kafka.consumer.enable-auto-commit=true

监听类配置

import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

@Component
public class MyKafkaConsumer {

    @KafkaListener(topics = {"test","dev"}) //在这里指定要监听的topic,可以监听多个
    public void listenToMessage(String message){
        System.out.println("使用注解监听到的消息"+message);
    }
}

运行效果

二、使用Kafka提供的原生java客户端中,消费者采while(true){…}用.poll()方式进行消息拉取

导入依赖

 <!--kafka-->
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>版本号</version>
        </dependency>

配置文件

这里以properties文件为例

#建立与kafka集群连接的host/port组,请通过控制台公网访问获取
bootstrap.servers=127.0.0.1:9092
#用来唯一标识consumer进程所在组的字符串,如果设置同样的group id,表示这些processes都是属于同一个consumer group
group.id=1
#键的序列化方式
key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
#值的序列化方式
value.deserializer=org.apache.kafka.common.serialization.StringDeserializer
#自动提交
enable.auto.commit=true

编写配置类

import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

public class MqsConsumer {

    public static final String CONFIG_CONSUMER_FILE_NAME = "mqs.sdk.consumer.properties";//配置文件

    private KafkaConsumer<Object, Object> consumer;

    MqsConsumer(String path)
    {
        Properties props = new Properties();
        try {
            InputStream in = new BufferedInputStream(new FileInputStream(path));
            props.load(in);
        }catch (IOException e)
        {
            e.printStackTrace();
            return;
        }
        consumer = new KafkaConsumer<Object, Object>(props);
    }

    public MqsConsumer()
    {
        Properties props = new Properties();
        try {
            props = loadFromClasspath(CONFIG_CONSUMER_FILE_NAME);
        }catch (IOException e)
        {
            e.printStackTrace();
            return;
        }
        consumer = new KafkaConsumer<Object, Object>(props);
    }
    public void consume(List topics)
    {
        consumer.subscribe(topics);
    }

    public ConsumerRecords<Object, Object> poll(long timeout)
    {
        return consumer.poll(timeout);
    }

    public void close()
    {
        consumer.close();
    }

/**
     * get classloader from thread context if no classloader found in thread
     * context return the classloader which has loaded this class
     *
     * @return classloader*/


    public static ClassLoader getCurrentClassLoader()
    {
        ClassLoader classLoader = Thread.currentThread()
                .getContextClassLoader();
        if (classLoader == null)
        {
            classLoader = MqsConsumer.class.getClassLoader();
        }
        return classLoader;
    }

/**
     * 从classpath 加载配置信息
     *
     * @param configFileName 配置文件名称
     * @return 配置信息
     * @throws IOException*/


    public static Properties loadFromClasspath(String configFileName) throws IOException
    {
        ClassLoader classLoader = getCurrentClassLoader();
        Properties config = new Properties();

        List<URL> properties = new ArrayList<URL>();
        Enumeration<URL> propertyResources = classLoader
                .getResources(configFileName);
        while (propertyResources.hasMoreElements())
        {
            properties.add(propertyResources.nextElement());
        }

        for (URL url : properties)
        {
            InputStream is = null;
            try
            {
                is = url.openStream();
                config.load(is);
            }
            finally
            {
                if (is != null)
                {
                    is.close();
                    is = null;
                }
            }
        }

        return config;
    }

采用while(true){...}进行消息拉取

import com.rococo.mqs.consumer.MqsConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Arrays;


public class KafkaController {
    @PostConstruct
    public void testConsumer() throws Exception {
        MqsConsumer consumer = new MqsConsumer();
        consumer.consume(Arrays.asList("test"));//监听的topic
        try {
            while (true){
                //timeout表示消费者在没有可用消息时愿意等待的最大时间。等待期间,一旦有消息到达,poll() 会立即返回,并从新开始计时。
                ConsumerRecords<Object, Object> records = consumer.poll(1000);
                System.out.println("the numbers of topic:" + records.count());
                for (ConsumerRecord<Object, Object> record : records)
                {
                    Object value = record.value();
                    System.out.println(value);
                }
            }
        }catch (Exception e)
        {
            // 异常处理
            e.printStackTrace();
        }finally {
            consumer.close();
        }
    }
}

执行效果

  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Kafka实现多线程消费者的方法主要有两种:使用 Kafka 自带的 Consumer API 或使用第三方的 Kafka 消费者库。 使用 Kafka 自带的 Consumer API,可以通过创建多个 Consumer 实例来实现多线程消费。每个 Consumer 实例都可以独立地消费一部分分区,多个 Consumer 实例一起消费整个 Topic。可以使用线程池来创建和管理 Consumer 实例,让每个线程处理一部分 Consumer 实例。需要注意的是,不同的 Consumer 实例之间需要避免重复消费同一个消息,需要使用不同的 Group ID 来区分不同的 Consumer 实例。 使用第三方的 Kafka 消费者库,比如 Apache Storm、Spring Kafka 等,这些库已经实现了多线程消费者的逻辑,可以直接使用库提供的接口来实现多线程消费。通常情况下,这些库会自动处理消息的分区和负载均衡等问题,简化了开发工作。 ### 回答2: 在Kafka实现多线程消费者的代码生成可以通过以下步骤完成: 1. 导入Kafka的相关依赖库,例如kafka-clients。 2. 创建KafkaConsumer对象,并设置所需的配置属性,如bootstrap.servers(Kafka集群的地址)、key.deserializer(键的反序列化器)和value.deserializer(值的反序列化器)。 3. 使用多线程并发消费的方式,可以使用Java提供的ExecutorService来创建线程池,设置合适的线程数量。 4. 使用线程池中的线程执行消费逻辑。要注意的是,为了确保多线程消费的正确性,需要为每个线程创建一个独立的KafkaConsumer对象,并采用不同的group.id。 5. 在消费线程的run方法中编写具体的消费逻辑,例如订阅所需的topic或者分区,然后使用poll方法从Kafka中获取消息。 6. 在获取到消息后,可以对消息进行处理,例如打印消息内容、进行业务处理等。 7. 当不再需要消费时,调用consumer.close()方法来关闭KafkaConsumer对象,释放资源。 示例代码如下所示: ```java 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 java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MultiThreadConsumer { private static final String BOOTSTRAP_SERVERS = "localhost:9092"; private static final String GROUP_ID = "group1"; private static final String TOPIC = "my_topic"; public static void main(String[] args) { Properties props = new Properties(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS); props.put(ConsumerConfig.GROUP_ID_CONFIG, GROUP_ID); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer"); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer"); final int numThreads = 3; ExecutorService executor = Executors.newFixedThreadPool(numThreads); List<RunnableConsumer> consumers = new ArrayList<>(); for (int i = 0; i < numThreads; i++) { RunnableConsumer consumer = new RunnableConsumer(props, TOPIC); consumers.add(consumer); executor.submit(consumer); } // 一段时间后停止消费 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 关闭消费者和线程池 for (RunnableConsumer consumer : consumers) { consumer.stop(); } executor.shutdown(); } static class RunnableConsumer implements Runnable { private final KafkaConsumer<String, String> consumer; private final String topic; public RunnableConsumer(Properties props, String topic) { this.consumer = new KafkaConsumer<>(props); this.topic = topic; } @Override public void run() { consumer.subscribe(Collections.singletonList(topic)); try { while (true) { ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000)); for (ConsumerRecord<String, String> record : records) { System.out.println("Received message: " + record.value()); // 处理消息 } } } finally { consumer.close(); } } public void stop() { consumer.wakeup(); } } } ``` 以上代码使用了固定线程数量的方式实现多线程消费者,在`main`方法中创建了一个具有3个线程的线程池,并为每个线程创建了一个独立的`RunnableConsumer`对象。消费逻辑在`run`方法中,通过调用`consumer.poll`方法来获取消息,并对消息进行处理。在不需要消费时,调用`stop`方法关闭消费者。 ### 回答3: 在Kafka实现多线程消费者,需要以下步骤: 1. 创建Kafka消费者,并设置相关属性,如Kafka集群的地址、反序列化器、消费者组等。 2. 实现一个消费者线程的类,该类需要继承Thread类并重写run()方法。在run()方法中,将使用创建的Kafka消费者进行消息消费的逻辑。 3. 在消费者线程的类中,可以通过消费者的poll()方法获取一批消息,并遍历处理每条消息。 4. 为了实现多线程消费,可以创建多个消费者线程,并将Kafka消费者对象传入线程的构造方法中。 5. 每个消费者线程将在独立的线程中运行,独立地从Kafka主题中消费消息。 6. 如果需要控制消费者线程的数量,可以使用线程池来管理消费者线程,以提供更好的伸缩性和灵活性。 7. 在处理每条消息时,可以根据业务需求进行相应的操作,如数据处理、持久化、发送到其他系统等。 8. 需要注意的是,Kafka消费者是无状态的,所以在多线程消费中,如果需要对消息的顺序进行保证,可以使用分区分配策略来保证消费者线程不会消费同一个分区的消息。 总结起来,实现Kafka多线程消费者的关键步骤是创建Kafka消费者、创建消费者线程类、使用线程池管理消费者线程,并在每个消费者线程中完成消息的消费逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值