Flink中的状态编程(简单案例)

前言

前几天学习了一下Flink的状态编程,于是自己想了个需求实现一下来加深对状态编程的理解。

需求是这样的:给出一个配置文件,用户在配置文件中定义库名,表名,以及 key列 和 value列等等,通过Flink从kafka中读取这样的数据,继而写入到Doris,在Doris中自动创建库创建表(该表根据配置文件构建,字段及字段类型与数据保持一致,是否分区表由配置文件决定)

状态编程

什么是有状态编程

通俗的说就是当发生一个或者多个事件时,将这些事件信息保存起来的操作,这些操作就是有状态编程。

什么情况下需要进行状态编程

比如在风控场景下,我们可能需要了解到哪些事件可能是一个风险事件,并进行报警,在传感器应用中,假设当连续的两次温度之差不在阈值范围内,我们需要进行报警时,那么我们可以将上一次的温度进行保存,与本次温度进行比较。

当然也有需要聚合的场景,比如每分钟每小时每天数据的聚合状态,我们可以将数据保存在状态中来进行操作。

如何进行状态编程

首先应该先了解状态是如何被保存的,这里简单介绍下Keyed State,KeyedState 是通过key-value形式进行保存的,这种状态仅仅适用于 Keyed Streams。当 kv流进入invoke方法中,我们定义的 KeyedState 会与 key 流中的key进行绑定,每个key都会携带自身的 状态。如下图:

在这里插入图片描述

代码实现

简单了解后,我们开始coding

① 首先开始构造一条KV流,可以通过keyBy()算子实现。

 SingleOutputStreamOperator<JSONObject> map = dataStreamSource.map(data -> {
            JSONObject jsonObject = JSONObject.parseObject(data);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String partitionDate = sdf.format(
                    new Date(Long.parseLong(jsonObject.get("date_received").toString().substring(0, 10)) * 1000L));
            jsonObject.put("partition_date", partitionDate);
            return jsonObject;
        });


        KeyedStream<JSONObject, String> result = map.keyBy(new KeySelector<JSONObject, String>() {
            @Override
            public String getKey(JSONObject value) throws Exception {
                return value.get("partition_date").toString();
            }
        });

② 在定义的Function中重写open方法。

当第一条数据到达时,首先需要对Doris中的库/表进行检查,是否存在库/表,如果不存在,那么需要建库建表,如果存在,那么直接执行插入。如果每个分区中每条数据来临时都要去连接数据库并检查,那么是不是有点过分了。此时,我将Doris中数据库/表状态保存在状态中,下次数据到来时,只需要通过读取状态中的值来进行判断是不是好很多呢?于是就采用了状态编程试一下。

    //定义一个状态,保存当前是否与数据库链接
    private ValueState<Boolean> flag;
    //定义一个状态,保存sql
    private ValueState<String> sql;
    //定义一个状态,保存列
    private ValueState<ArrayList<String>> colList;
		@Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        ValueStateDescriptor<Boolean> valueStateDescriptor = new ValueStateDescriptor<>("isExistsTable", Boolean.class);
        ValueStateDescriptor<String> sqlValueStateDescriptor = new ValueStateDescriptor<>("sql", String.class);
        ValueStateDescriptor<ArrayList<String>> colListValueStateDescriptor =
                    new ValueStateDescriptor("colList", ArrayList.class);

        flag = getRuntimeContext().getState(valueStateDescriptor);
        sql = getRuntimeContext().getState(sqlValueStateDescriptor);
        colList = getRuntimeContext().getState(colListValueStateDescriptor);
        //获取Connection
        conn = getConnection();

    }

在open方法中先创建了三个valueStateDescriptior,第一个用来保存是否存在表,第二个用来保存首次根据数据构建的sql,第三个保存首次根据数据获取到的所有列。

② 在invoke()方法中使用state

@Override
    public void invoke(JSONObject value, Context context) throws Exception {
        super.invoke(value,context);
        //获取状态,是否已经存在表
        if(flag.value() == null || !flag.value()){
            logger.info("check table ...");
            cols.addAll(value.keySet());
            colList.update(cols);
            sql.update(SQLUtil.getInsertSQL(databaseName,tableName,colList.value()));
            boolean existsTable = DorisClient.isExistsTable(databaseName, tableName);
            if(!existsTable){
                logger.info("create table ...");
                boolean isCreate = creatDatabase(databaseName, tableName, keyCols, partitionKey, distributeKey, valueCols);
                flag.update(isCreate);
            }else{
                flag.update(true);
            }
        }

        if(sql.value() != null ){
            preparedStatement = conn.prepareStatement(sql.value());
        }

        int i =0;
        for (String key : colList.value()) {
            i++;
            preparedStatement.setObject(i,value.getString(key));
        }
        //数据刷写
        flush();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值