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,我说的对吗。

相关推荐
2601_9620559721 小时前
跟据spring boot版本,查看对应的tomcat,并查看可支持的tomcat的版本范围
spring boot·后端·tomcat
Lost of 程序猿1 天前
ASP.NET Core API 幂等性设计深度实战:从一次重复申领事故说起
后端·asp.net
QQ_21696290961 天前
【源码编号:project79475】SpringBoot校内二手交易平台:商品发布、分类检索、留言交流、订单管理全流程实战
java·spring boot·后端
郑州光合科技余经理1 天前
本地生活服务系统:模块边界与结算字段怎么拆
java·开发语言·前端·后端·系统架构·uni-app·php
IT_陈寒1 天前
Vue的嵌套组件竟然吃掉了我的事件?
前端·人工智能·后端
大辉狼_音频架构1 天前
进阶:从源码编译 SOF 固件与 topology
后端
大辉狼_音频架构1 天前
上板:让 SOF 在 FRDM-i.MX8MP 上跑起来
后端
大辉狼_音频架构1 天前
FRDM-IMX8MP UUU 烧录 eMMC 指南
后端
运行时异常1 天前
【WMS 仓储系统集成 AI Agent 实战】第 3 讲:Spring Security 6 + JWT——addFilterBefore 一词之差,全站 401
java·后端
LinMINGJing0071 天前
PageHeaderData:Page 的页面头
后端