Springboot快速整合Kafka consumer方案

前言

最近搞了一个多线程灵活配置kafka消费链接的演示demo,过程还比较有意思,由于有一阵子没搞kafka了简单记录一下。

@KafkaListener

在类或方法上使用@KafkaListener注解定义消费者是最简单明了的一种方式,适用于处理简单的单个topic或者少量topic的情况,毕竟使用注解无法灵活扩展消费者。

虽然topic支持从配置文件中读取,且支持多个topic读取,但在某些场景下比较笨重,比如需要根据前端交互增加kafka消费者,使用注解显然实现不了,不可能前端注册一个kafka链接后端服务新生成一个class,在这个场景下用这方案在技术评审上会被喷死。

typescript 复制代码
@Component
public class KafkaConsumer {

    @KafkaListener(topics = "myTopic",  groupId = "test1")
    public void consume(String message) {
        System.out.println("Received message: " + message);
    }
}

这种比较适合模块与模块之间,或者服务之间,约定好topic建立通信的场景,比较简单且实用。

KafkaConsumer

根据刚才的场景,产品交互上有一个注册kafka链接的地方,前端填写完注册信息后传递给后端,后端需要生成一个kafka消费者不断监听数据,这种场景如何实现。

首先有多个用户创建消费链接,显然是一个多线程的场景,我们需要一个池子去管理kafka的链接,又是多线程又是池化,首先想到的线程池去管理,我们把kafka的消费链接和client做成线程私有的,启动的时候用线程池提交,这样线程池中的每一个线程都是一个kafka client。

我们使用apache.kafka包提供的现成的org.apache.kafka.clients.consumer.KafkaConsumer

xml 复制代码
 <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka-clients</artifactId>
        <version>2.4.0</version>
</dependency>

首先定义一个class实现Runable接口,每次一提Runable就想起校招的时候问的最多的就是实现多线程有哪几种方式。。。。

typescript 复制代码
@Slf4j
@Component
public class KafkaConsumerTask implements Runnable {

    // 每个线程维护私有的KafkaConsumer实例
    @Getter
    private KafkaConsumer<String, String> consumer;

    @Getter
    private String topicName;


    /**
     * 封装必要信息
     * @param bootServer 生产者ip
     * @param groupId 分组信息
     * @param topic  订阅主题
     */
    public KafkaConsumerRunnable(String bootServer, String groupId, String topic) {
        topicName = topic;
        Properties props = new Properties();
        props.put("bootstrap.servers", bootServer);
        props.put("group.id", groupId);
        props.put("enable.auto.commit", "false");
        props.put("auto.commit.interval.ms", "100");  //自动提交时间间隔 毫秒单位
        props.put("session.timeout.ms", "30000");
        props.put("auto.offset.reset", "latest");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");  //键反序列化方式
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        this.consumer = new KafkaConsumer<>(props);
    }


    @Override
    public void run() {
        consumer.subscribe(Arrays.asList(topicName));
        Thread.currentThread().setName(topicName);
        try {
            while (true) {
                ConsumerRecords<String, String> records = consumer.poll(100);   // 本例使用100ms作为获取超时时间
                for (ConsumerRecord<String, String> record : records) {
                    String value = record.value();
                    log.info("线程 {} 消费kafka数据 -> {} \n 偏移量offset -> {} \n 分区partition -> {}",
                    
                    Thread.currentThread().getName(), value, record.offset(), record.partition());
                    
                    // 2、反序列化数据
                    HashMap resMap = JSON.parseObject(value, HashMap.class);
                    log.info("resMap -> {}", resMap);
                }
            }
        } catch (WakeupException) {
            log.info("consumer ");
        } catch (Exception e) {
            throw new HoraException("数据消费异常 -> ", e);
        } finally {
           consumer.commitAsync();
        }
    }

    // 退出后关掉consumer
    public void shutDown() {
        consumer.wakeup();
    }
}

代码贴出来结构就很明朗了,consumer每个线程私有变量,通过构造方法传参的方式初始化consumer,run方法里while true 去常驻监听数据,当需要退出后外部调用shutDown() 方法,内部consumer.wakeup() 方法调用后consumer会抛出异常跳出while循环结束任务。

提交任务时可以用线程池去提交,也可以自己搞个map维护链接都行,由于是个简单的演示demo,细节做的都不到位,还需要考虑下offset的维护,consumer链接挂了之后如何重新拉起,数据如何不丢失等等等问题。

不过工作又不是面试,xdm,我说的对吗。

相关推荐
林太白2 小时前
❤Node09-用户信息token认证
数据库·后端·mysql·node.js
骆晨学长2 小时前
基于Springboot的助学金管理系统设计与实现
java·spring boot·后端
蒙娜丽宁2 小时前
深入理解Go语言中的接口定义与使用
开发语言·后端·golang·go
AskHarries3 小时前
java使用ByteBuffer进行多文件合并和拆分
java·后端
不染_是非3 小时前
Django学习实战篇六(适合略有基础的新手小白学习)(从0开发项目)
后端·python·学习·django
代码对我眨眼睛4 小时前
springboot从分层到解耦
spring boot·后端
The Straggling Crow4 小时前
go 战略
开发语言·后端·golang
ai安歌4 小时前
【JavaWeb】利用IDEA2024+tomcat10配置web6.0版本搭建JavaWeb开发项目
java·开发语言·后端·tomcat·web·intellij idea
尘浮生4 小时前
Java项目实战II基于Java+Spring Boot+MySQL的作业管理系统设计与实现(源码+数据库+文档)
java·开发语言·数据库·spring boot·后端·mysql·spring
程序员阿鹏5 小时前
ArrayList 与 LinkedList 的区别?
java·开发语言·后端·eclipse·intellij-idea