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

相关推荐
独孤九剑打醒他1 天前
双层Master-Worker软硬协同调度架构:从根源解决分布式数据一致性难题
后端·嵌入式硬件·硬件架构·硬件工程
不会c+1 天前
02-SpringBoot配置文件
java·spring boot·后端
雨辰AI1 天前
生产级实战:人大金仓 V9 标准化运维手册(日常巡检 + 监控告警 + 应急处置)
java·运维·数据库·后端
TeamDev1 天前
JxBrowser 9.3.0 版本发布啦!
java·后端·c#·混合应用·jxbrowser·浏览器控件·异步媒体设备
陈随易1 天前
Rust、Golang、MoonBit 编译成 WASM,体积和速度差距有多大?
前端·后端·程序员
IT_陈寒1 天前
Python多线程的坑,我居然现在才踩到
前端·人工智能·后端
魏祖潇1 天前
DDD 完整指南——AI 时代工程师的第一道秩序分水岭
人工智能·后端
im_lanny1 天前
如何给 Agent 打造“最强大脑“?深度解析短期记忆与长期记忆的分层设计
后端
Fanta丶1 天前
2.Activiti表结构介绍 类关系
后端
触底反弹1 天前
AI Tool Use 深度解析:大模型是如何"突破物理限制"调用外部工具的?
javascript·人工智能·后端