Flink SQL 实战 (2):Tabel API & SQL 自定义 Sink函数

实战篇-2:Tabel API & SQL 自定义 Sink函数

引子:匪夷所思的Bool数据

在上一篇实战博客,我们使用Flink SQL API编写了一个基本的WordWithCount计算任务

我截取了一段控制台输出:

2> (true,1,Huawei)
5> (false,1,Vivo)
5> (true,2,Vivo)
2> (false,1,Huawei)
2> (true,2,Huawei)
3> (true,1,Xiaomi)
3> (false,1,Xiaomi)
3> (true,2,Xiaomi)

不难发现我们定义的表数据本应该是只有LONG和STRING两个字段,但是控制台直接输出Tabel的结果却多出一个BOOL类型的数据。而且同样计数值的数据会出现true和false各一次。

在官方文档关于retractstreamtablesink的介绍中, 该表数据将被转换为一个累加和收回消息流,这些消息被编码为Java的 Tuple2<Boolean,T>类型。第一个字段是一个布尔标志,用于指示消息类型(true表示插入,false表示删除)。第二个字段才是sink的数据类型。

所以在我们的WordWithCount计算中,执行的SQL语句对表的操作不是单纯insert插入,而是每执行一次sink都会在sink中执行 删除旧数据插入新数据 两次操作。


用于Flink Tabel环境的自定义 Sources & Sinks函数和DataStream API思路是差不多的,如果有编写DataStream APISources & Sinks函数的经验,编写用于Flink Tabel环境的自定义函数是较容易理解和上手的。

定义TableSink

现在我们要给之前的WordWithCount计算任务添加一个自定义Sink

在这里插入图片描述

flink.table.sinks提供了有三种继承 StreamTableSink类的接口:

  • AppendStreamTableSink: 仅发出对表插入的更改
  • RetractStreamTableSink:发出对表具有插入,更新和删除的更改 ,消息被编码为 Tuple2<Boolean,T>
  • UpsertStreamTableSink: 发出对表具有插入,更新和删除的更改 ,消息被编码为 Tuple2<Boolean,T>,表必须要有类似主键的唯一键值( 使用setKeyFields(方法),不然会报错

因为在我们的WordWithCount计算中,执行的SQL语句对表的操作不是单纯insert插入,所以我们需要编写实现RetractStreamTableSink的用户自定义函数:

public class MyRetractStreamTableSink implements RetractStreamTableSink<Row> {
   private TableSchema tableSchema;
   //构造函数,储存TableSchema
   public MyRetractStreamTableSink(String[] fieldNames,TypeInformation[] typeInformations){
       this.tableSchema=new TableSchema(fieldNames,typeInformations);
   }
   //重载
    public MyRetractStreamTableSink(String[] fieldNames,DataType[] dataTypes){
        this.tableSchema=TableSchema.builder().fields(fieldNames,dataTypes).build();
    }
    //Table sink must implement a table schema.
    @Override
    public TableSchema getTableSchema() {
        return tableSchema;
    }
    @Override
    public DataStreamSink<?> consumeDataStream(DataStream<Tuple2<Boolean, Row>> dataStream) {
        return dataStream.addSink(new SinkFunction<Tuple2<Boolean, Row>>() {
            @Override
            public void invoke(Tuple2<Boolean, Row> value, Context context) throws Exception {
               //自定义Sink
                // f0==true :插入新数据
                // f0==false:删除旧数据
                if(value.f0){
                //可以写入MySQL、Kafka或者发HttpPost...根据具体情况开发
                    System.out.println(value.f1);
                }
            }
        });
    }
    
    //接口定义的方法
    @Override
    public TypeInformation<Row> getRecordType() {
        return new RowTypeInfo(tableSchema.getFieldTypes(),tableSchema.getFieldNames());
    }
    //接口定义的方法
    @Override
    public TableSink<Tuple2<Boolean, Row>> configure(String[] strings, TypeInformation<?>[] typeInformations) {
        return null;
    }
    //接口定义的方法
    @Override
    public void emitDataStream(DataStream<Tuple2<Boolean, Row>> dataStream) {
    }
   
}

吐槽一下,目前使用1.9.0版本API,在注册source Tabel都用 TypeInformation[]表示数据类型。

而在编写Sink时使用TypeInformation[]的方法都被@Deprecated,提供了Builder方法代替构造,使用DataType[]TableSchema.builder().fields的参数表示数据类型,统一使用 TypeInformation[]表示数据类型比较潇洒,当然使用 TableSchema.builder()方法有对空值的检查,更加***可靠***。

所以写了重载函数:我全都要

使用自定义Sink,直接用new定义Tabel的结构简化了代码:

  import kmops.models.MyRetractStreamTableSink;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.java.StreamTableEnvironment;
import org.apache.flink.table.sinks.RetractStreamTableSink;
import org.apache.flink.table.sources.CsvTableSource;
import org.apache.flink.table.sources.TableSource;
import org.apache.flink.types.Row;

public class CustomSinkJob {
    public static void main(String[] args) throws Exception {
        //初始化Flink执行环境
        StreamExecutionEnvironment blinkStreamEnv=StreamExecutionEnvironment.getExecutionEnvironment();
        EnvironmentSettings blinkStreamSettings= EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
        StreamTableEnvironment blinkStreamTabelEnv= StreamTableEnvironment.create(blinkStreamEnv,blinkStreamSettings);
        //获取Resource路径
        String path= CustomSinkJob.class.getClassLoader().getResource("list.txt").getPath();

        //注册数据源
        TableSource fileSource=new CsvTableSource(path,new String[]{"word"},new TypeInformation[]{Types.STRING});
        blinkStreamTabelEnv.registerTableSource("flieSourceTable",fileSource);

       //注册数据汇(Sink)
        RetractStreamTableSink<Row> retractStreamTableSink=new MyRetractStreamTableSink(new String[]{"_count","word"},new TypeInformation[]{Types.LONG,Types.STRING});
        //或者
        //RetractStreamTableSink<Row> retractStreamTableSink=new MyRetractStreamTableSink(new String[]{"_count","word"},new DataType[]{DataTypes.BIGINT(),DataTypes.STRING()});
        blinkStreamTabelEnv.registerTableSink("sinkTable",retractStreamTableSink);

        //执行SQL
        Table wordWithCount = blinkStreamTabelEnv.sqlQuery("SELECT count(word) AS _count,word FROM flieSourceTable GROUP BY word ");

        //将SQL结果插入到Sink Table
        wordWithCount.insertInto("sinkTable");
        blinkStreamTabelEnv.execute("BLINK STREAMING QUERY");
    }
}

输出结果:

1,OnePlus
1,Oppo
2,Oppo
2,OnePlus

GitHub

源码已上传至GitHub

https://github.com/StarPlatinumStudio/Flink-SQL-Practice

下篇博客干货极多
https://blog.csdn.net/qq_35815527/article/details/103866923

To Be Continue=>

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值