kafka学习系列:根据数据库获取主题列表,并根据主题启动不同的消费者,实现主题间消费互不影响

场景

公司自研框架开发中,使用了kafka组件,需要根据不同的业务,监听不同的主题。每个业务的主题是动态可变的,所以监听的主题也是需要根据变化动态地重启。

环境

软件版本
JDK8
Kafka2.0.1
spring-boot2.1.8.RELEASE
Centos7

正文

项目是基于spring-cloud而搭建的微服务框架,所以我们这边直接引用了spring-kafka项目来进行快速上手使用kafka

一、项目依赖

项目依赖如下:

<dependency>
  <groupId>org.springframework.kafka</groupId>
  <artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.kafka</groupId>
  <artifactId>spring-kafka-test</artifactId>
  <scope>test</scope>
</dependency>

二、项目配置

spring.kafka.consumer.group-id=test
spring.kafka.bootstrap-servers=test01:9092,test02:9092,test03:9092
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.listener.ack-mode=manual_immediate
spring.kafka.consumer.enable-auto-commit=false
spring.kafka.consumer.max-poll-records=50

三、正文

原本是使用KafkaListener来进行主题的监听,原本里面的topicPattern是可以实现动态的监听,但是设置的主题名称就得有了固定的模式了。另外,这里是有多个模块功能,而模块功能是不确定的,是动态新增的,并且需要各自消费自己的主题,不能相互影响。所以这个时候,只使用KafkaListener就稍显不足了,也没办法新增一个模块功能,就新增一个KafkaListener的方法。所以,研究了spring-kafka之后,使用了ConcurrentMessageListenerContainer来进行动态的新建及启动。具体核心的代码,请看下面:

/**
 * 生成对应采集实例的主题监听容器
 * @param itemId 模块ID
 * @param sItemName 模块名称
 * @param topics 对应的主题
 * @return 容器
 */
public ConcurrentMessageListenerContainer<Integer, String> createContainer(String itemId,String sItemName,List<String> topics) {
    String[] topicArray = topics.toArray(new String[topics.size()]);
    boolean isNew = true;
    if (containerMap.containsKey(itemId)) {
        String[] oldTopics = containerMap.get(itemId).getContainerProperties().getTopics();
        Boolean isDifferent = compareTwoList(oldTopics, topicArray);
        if (isDifferent) {
            log.info("itemID({}) 监听主题有所变动,当前监听主题为:{},变动后的主题为:{},准备重启", itemId, String.join(",", oldTopics), String.join(",", topicArray));
            isNew = true;
        }else{
            isNew = false;
        }
    }

    if (isNew) {
        if (containerMap.containsKey(itemId)) {
            containerMap.get(itemId).stop();
        }
    }

    ContainerProperties containerProps = new ContainerProperties(topicArray);
    int concurrency = Integer.parseInt(SysConfigUtil.getProperty("spring.kafka.listener.concurrency", "3"));
    log.info("主题 {} 的消费并行度设置为{}",String.join(",",topicArray),concurrency);

    // 绑定处理
    containerProps.setMessageListener((AcknowledgingMessageListener<Integer, String>) (message, ack) -> {
        log.info("消费:{}",message);
        // 提交偏移量
        ack.acknowledge();
    });
    // 设置提交状态
    containerProps.setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
    // 生成实例
    ConcurrentMessageListenerContainer<Integer, String> container =
            new ConcurrentMessageListenerContainer<>(consumerFactory, containerProps);
    // 设置并行度
    container.setConcurrency(concurrency);
    // 设置应用名称
    container.setBeanName(sItemName);
    // 启动实例
    container.start();

    return container;
}

参考链接

spring-kafka-docs

总结

通过研究spring-kafka的文档和源码,最后实现了这个功能。

随缘求赞

如果我的文章对大家产生了帮忙,可以在文章底部点个赞或者收藏;
如果有好的讨论,可以留言;
如果想继续查看我以后的文章,可以左上角点击关注
拜拜

posted on 2022-11-29 18:40  枫夜求索阁  阅读(142)  评论(0)    收藏  举报

导航