flink- 动态获取topic

1. 问题

 flink 版本一升级,好多api 都红了, 记一下日志。

flink消费kafka有几种指定topic 的方式:

  • 指定单独的topic
  • 指定topic 列表
  • 指定正则表达式

有时这些还不满足功能, 我就想根据自己的想法,从外部存储里获取我想要的topic,这样可以不重启代码的前提下,最大的灵活性消费自己想要的topic。

2.方案

关键在这类,

模仿 getTopicListSubscriber 新建一个方法,  list 是从外部存储读取的。

别模仿list这个, 模仿getTopicPatternSubscriber, 防止外部提供的topic 不存在,整体报错。

然后在KafkaSourceBuilder 中重载一个setTopicPattern 方法调用上面方法就可以了。

注意设置分区发现时间:setProperty("partition.discovery.interval.ms","10000")



package org.apache.flink.connector.kafka.source.enumerator.subscriber;

import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.common.TopicPartition;

import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

public interface KafkaSubscriber extends Serializable {

  
    Set<TopicPartition> getSubscribedTopicPartitions(AdminClient adminClient);

    // ----------------- factory methods --------------

    static KafkaSubscriber getTopicListSubscriber(List<String> topics) {
        return new TopicListSubscriber(topics);
    }

    static KafkaSubscriber getTopicPatternSubscriber(Pattern topicPattern) {
        return new TopicPatternSubscriber(topicPattern);
    }
    static KafkaSubscriber getTopicFunctionSubscriber(String param) {
        return new TopicFunctionSubscriber(param);
    }

    static KafkaSubscriber getPartitionSetSubscriber(Set<TopicPartition> partitions) {
        return new PartitionSetSubscriber(partitions);
    }
}

3. 使用场景

想做一个通用功能,起多个任务,把topic灵活分配给这些任务,把kafka中的数据写进es。

根据kafka 的数据量,起n个任务, 每个任务根据 KafkaSource 构建时指定的参数

从mysql里读取消费哪些topic 。

4.  flink  sql  动态topic

flink  sql 中也想实现上面的效果,关键在这俩类:

KafkaDynamicTableFactory : 主要是验证输入配置项。
KafkaDynamicSource,  

关键代码在:380行

  final KafkaSourceBuilder<RowData> kafkaSourceBuilder = KafkaSource.builder();

        if (topics != null) {
            kafkaSourceBuilder.setTopics(topics);
        } else {
            kafkaSourceBuilder.setTopicPattern(topicPattern);
        }

把从sql 传进来的参数, 传递给上面api 的 KafkaSourceBuilder

5. 遗留问题

怎么把获取外部topic 的逻辑传递进去。

写api的方式都好办, sql 开发时怎么把取topic的逻辑传递给 自己定义的KafkaSubscriber 子类。

  •  param  :api 和 sql  都可以传递这个参数
  • getTopic: 这是个自定义函数, 利用param 参数获取哪些topic 需要消费的函数。 api的方式可以在使用前把函数内容传给TopicFunctionSubscriber 。   sql方式怎么把这个函数传过去。下面赋值方式只适合api , 不是纯sql方式。
  • 试试反射吧, 可以,参数传递类名进去。
  • 还是有缺陷, 最好是穿个方法的参数进去。 本打算是对现有topic 分组,然后根据组名字获取需要实收哪些topic
----函数接口
@FunctionalInterface
public interface GetTopic {
    List<String> getTopic( String param);
}

-------jar包外实现, api直接写, sql方式的话得单独上传一个jar 包了

mport org.apache.flink.connector.kafka.source.enumerator.subscriber.GetTopic;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class GetTopic1  implements GetTopic , Serializable {
    @Override
    public List<String> getTopic(String param) {

        List<String> list = new ArrayList<>();
        list.add("topic");
   
        return  list;
    }
}


------'topic_function' = 'sql.GetTopic1#222'  类名+ 方法参数
----sql 方式调用
   String source="CREATE TABLE source_table (\n" +
                "    op  string, \n" +
                "    data ROW<empUid string  >   , \n" +
                "    occur_time  string  ,\n" +
                "   `ts` TIMESTAMP(3) METADATA FROM 'timestamp' , \n " +
                "   WATERMARK FOR ts AS ts \n " +

                ")\n" +
                "with (\n" +
                "    'connector' = 'kafka',\n" +

                "    'topic_function' = 'sql.GetTopic1#222',\n" +
                "    'properties.bootstrap.servers' = '10.86.25.6:9092',\n" +
                "    'properties.group.id' = 'testGroup1111',\n" +
                "    'scan.startup.mode' = 'latest-offset',\n" +
                "    'scan.topic-partition-discovery.interval'='10s' , \n  " +
                "    'format' = 'json'\n" +
                ")";

------api方式调用
   KafkaSource<User> source = KafkaSource.<User>builder()
                .setBootstrapServers("")
                .setTopicFunction("sql.GetTopic1#11")    
                .setGroupId("my-group")
                .setProperty("partition.discovery.interval.ms", "10000")
                .setStartingOffsets(OffsetsInitializer.latest())
//                .setDeserializer(new MyJsonSchema("source_table"))
                .setValueOnlyDeserializer(new MyUserSchema())   //  自定义的 schema 要和source上声名的一致
//                .setValueOnlyDeserializer(new SimpleStringSchema())

                .build();
public class TopicFunctionSubscriber implements KafkaSubscriber {
    private static final long serialVersionUID = -7471048577725467797L;
    private static final Logger LOG = LoggerFactory.getLogger(TopicFunctionSubscriber.class);
    private GetTopic  getTopic;
    private String param;
    public TopicFunctionSubscriber(String param)  {
        this.param=param.split("#")[1];
        try {

            this.getTopic = (GetTopic) Class.forName(param.split("#")[0]).newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public Set<TopicPartition> getSubscribedTopicPartitions(AdminClient adminClient) {
        LOG.debug("Fetching descriptions for all topics on Kafka cluster");
        final Map<String, TopicDescription> allTopicMetadata = getAllTopicMetadata(adminClient);
       List<String> list=getTopic.getTopic(this.param );
        System.out.println("消费的topic:"+list.toString());

        Set<TopicPartition> subscribedTopicPartitions = new HashSet<>();

        allTopicMetadata.forEach(
                (topicName, topicDescription) -> {
                    if (list.contains(topicName)) {
                        for (TopicPartitionInfo partition : topicDescription.partitions()) {
                            subscribedTopicPartitions.add(
                                    new TopicPartition(
                                            topicDescription.name(), partition.partition()));
                        }
                    }
                });

        return subscribedTopicPartitions;
    }


}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值