记录一次使用Flink对Kafka数据流与Mysql广播流关联使用的踩坑

记录一次使用Flink对Kafka数据流与Mysql广播流关联使用的踩坑

接到一个这样的需求:kafka接收实时数据流,需要根据MySQL中的一张表的某个字段进行过滤,然后再写入到MySQL中

Kafka中的数据格式为json格式,跟后端沟通发现MySQL中配置表基本上是不会变化的,但是保险起见还是使用Flinkcdc了,读取MySQL形成配置流在广播出去

首先,准备一些测试数据,读取kafka数据和mysql数据,想着简单一点直接flinksql建表读取

tenv.executeSql("create table AISData(" +
        "   mmsi string," +
        "   heading string," +
        "   rot string," +
        "   course string," +
        "   lon double," +
        "   `time` string," +
        "   create_at string," +
        "   lat double," +
        "   speed string," +
        "   status string," +
        "   proTime as PROCTIME()" +
        ")with(" +
        "   'connector' = 'kafka', " +
        "   'properties.bootstrap.servers' = '192.168.1.102:9092', " +
        "   'properties.group.id' = 'AISData02', " +
        "   'topic' = '" + "test-ais" + "', " +
        "   'scan.startup.mode' = 'earliest-offset', " +
        "   'format' = 'json' " +
        ")");

Table t1 = tenv.sqlQuery("select mmsi,\n" +
        "       heading,\n" +
        "       rot,\n" +
        "       course,\n" +
        "       lon,\n" +
        "       `time`,\n" +
        "       create_at,\n" +
        "       lat,\n" +
        "       speed,\n" +
        "       status\n" +
        "from AISData");
tenv.createTemporaryView("t1",t1);
DataStream<AISdata> aiSdataDataStream = tenv.toDataStream(t1, AISdata.class);
//TODO 使用flinkcdc读取mysql中配置信息
MySqlSource<String> mySqlSource = MySqlSource.<String>builder().hostname("localhost")
        .port(3306)
        .databaseList("aisdata")
        .tableList("aisdata.vessel_info_main")
        .username("root")
        .password("123456")
        .deserializer(new JsonDebeziumDeserializationSchema())
        .startupOptions(StartupOptions.initial())
        .build();

DataStreamSource<String> mysqlSource = env.fromSource(mySqlSource,
        WatermarkStrategy.noWatermarks(), "MysqlSource");
//筛选出配置流中的关键判定字段
SingleOutputStreamOperator<String> map = mysqlSource.map(new MapFunction<String, String>() {
            @Override
            public String map(String value) throws Exception {
                JSONObject jsonObject = JSON.parseObject(value);
                Tab after = jsonObject.getObject("after", Tab.class);
                return after.getMmsi();
            }
        });
MapStateDescriptor<String, ship> stringshipMapStateDescriptor = new MapStateDescriptor<String, ship>("state",String.class, ship.class);
//mysql形成广播流
BroadcastStream<String> broadcast = map.broadcast(stringshipMapStateDescriptor);
//主流关联广播流
BroadcastConnectedStream<AISdata, String> connect = aiSdataDataStream.connect(broadcast);
//业务处理逻辑
SingleOutputStreamOperator<AISdata> singleOutputStreamOperator = connect.process(new MyBroadcastFunctioncdc(stringshipMapStateDescriptor));
singleOutputStreamOperator.print("result");

到这里一切正常,其中关联逻辑非常简单,让广播流进入状态,然后Kafka流根据状态中的特定字段进行判定,有就输出没有就扔掉
在这里插入图片描述

然后神奇的事情发生了

因为测试我是用自己本地mysql,版本是5.1.27,当我使用后端的生产环境数据库MySQL8时,发现无论无何这个广播流在刚开始没有加载到状态中,这样会导致一部分数据漏掉。那怎样让广播流优先加载到状态中?经过搜索,发现一篇文章说可以采取主流阻塞的方法,为此我添加一个阻塞的逻辑,主流第一个元素过来阻塞5秒,之后再去处理这第一个元素

   	@Override
    public void processElement(AISdata value, ReadOnlyContext ctx, Collector<AISdata> out) throws Exception {
        String mmsi = value.getMmsi();
        ReadOnlyBroadcastState<String, ship> broadcastState = ctx.getBroadcastState(broadcastLoadedState);
        if (anInt == 1){
            Thread.sleep(5000);
            listState.add(value);
        }
        if (anInt != 1){
            if (anInt == 2){
                for (AISdata aiSdata: listState){
                    if (broadcastState.contains(aiSdata.getMmsi())){
                        out.collect(aiSdata);
                        anInt++;
                    }
                }
            }
            if (anInt == 3){
                if (broadcastState.contains(mmsi)){
                    System.out.println("--------");
                    out.collect(value);
                }
            }
        }
        if (anInt==1)
            anInt++;
    }

但是发现并没有用,百思不得其解,这个广播流为什么进入不了状态呢?最终无奈打电话摇人

我:大佬麻烦帮我看看这个广播流为啥关联不上呢?我还特意阻塞了主流
大佬:你先这样这样在这样………你看你的阻塞是不起作用的,你从哪里学的?
我:csdn
大佬:呵,csdn上面的东西你也信!
我:那为啥在mysql5不加阻塞也可以关联到广播流呢?
大佬:MySQL8加了很多安全校验的内容,初始化速度很慢,而且你那个5的多试几次你会发现有时候mysql流也比Kafka流慢
我:那这个主流阻塞为什么不行呀,看起来好像没毛病啊
大佬:你要搞清楚,你的这个process算子在运行时相当于一个单线程,所以你的操作相当于在单线程里面阻塞当前线程,你说有没有用?
我:Flink不是有并行度吗,一个任务不是可以多个线程一起执行吗?
大佬:唉!Flink的多并行内存是严格隔离的?一个task执行任务其实还是相当于单线程,打个比方多个task在执行你的那个process算子时,每个task都会阻塞。而且要用阻塞也不是在算子里面阻塞啊,要么就在主流初始化的时候阻塞。
我:那意思是在Kafka读取之后用map在阻塞一下就可以达到目的吗?
大佬:这可不一定,你得去算子链里面看下
我:(⊙o⊙)?强

最后,这个困扰我许久的问题终于得到解决。之后的第一感受就是应该转行了,Flink没有入门!

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值