Flink入门第九课:DataStream api的state操作

package com.atguigu.Dstate;

import com.atguigu.Fbeans.SensorReading;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.common.state.*;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.checkpoint.ListCheckpointed;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

import java.util.Collections;
import java.util.List;

/**
 *在flink中,所谓的状态就是一个本地变量,flink会自动使用managed state进行转态管理,你也可以使用raw state进行
 *      自定义开发。相比raw state,managered state支持更加丰富的数据类型,且不需要自己序列化。状态的定义都是在
 *      算子的匿名函数内。
 *operator state:
 *      状态的访问范围是算子内,匿名函数要实现ListCheckpointed接口和其内部的两个方法。也可以实现其他的接口,用得不多。
 *      分类:
 *          列表状态:将状态表示为一组数据的列表。
 *          联合列表状态:将状态表示为一组数据的列表。只是从检查点故障恢复时和列表状态不一样。
 *          广播状态:如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态。
 * keyed state:
 *      状态访问范围只是跨算子的同一个key,使用前必须keyBy。
 *      因为需要通过getRuntimeContext来给状态初始化,所以匿名函数需要继承富函数。
 *      分类:
 *          值状态:将状态表示为单个值。private ValueState list;   getRuntimeContext().getState(...)
 *          列表状态:将状态表示为一组数据的列表。 private ListState list;  getListState(....)
 *          映射状态:将状态表示为一组key-value对。private MapState list;     getMapState(....)
 *          集合状态(reducing/aggregating):将状态表示为一个用于聚合操作的列表。
 *                                          private ReducingState list;   getReducingState(....) 
状态常合定时器一起使用,定时器触发后应当清除状态。如果配置了allowedLateness,在事件时间到了
allowedLateness规定的延迟时间,再清除状态。
 *状态后端:
 *  负责状态的存储、访问和维护,检查点的写入都由一个可插拔的状态后端组件决定。
 *  分类:
 *      MemoryStateBackend()   state存tm内存, 检查点存jm内存。仅开发使用。
 *      FsStateBackend("检查点uri地址")   state存tm内存, 检查点存外部文件系统。生产可用。
 *      RocksDBStateBackend("检查点uri地址")   state存tm上RocksDB数据库(内存+磁盘),检查点存RocksDB数据库或者外部文件系统,
 *          生产可用。需要引入RocksDB的依赖。适用于超大状态且对状态读写性能要求不高的的作业。
 *env.setStateBackend(new MemoryStateBackend())  其他的类似,可以传参指定异步还是同步,或者外部文件系统路径。
 *      这种设置方法已经显示过期。
 */
public class AStateTest {
    public static void main(String[] args) throws Exception {
        //创建环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        //读取数据并包装成pojo
        DataStreamSource<String> inputStream = env. socketTextStream("localhost",7777);
        DataStream<SensorReading> mapStream = inputStream.map(line -> {
            String[] splits = line.split(",");
            return new SensorReading(new String(splits[0]), new Long(splits[1]), new Double(splits[2]));
        });

        /**
         * 通过map方法测试算子状态和键控状态
         * 键控状态必须先分组
         *
         */
        SingleOutputStreamOperator<Integer> result1 = mapStream.map(new MyOperatorState());//算子状态
        SingleOutputStreamOperator<Integer> result2 = mapStream.keyBy(SensorReading::getId)
                .map(new MyKeyedValueState());

        //输出与执行
        result1.print("测试算子状态");
        result2.print("测试键控状态");
        env.execute("测试状态");
    }

    /**
     * 算子状态的方法,必须实现ListCheckpointed接口,当然也可以其他的。用到再百度。
     *    ListCheckpointed接口的泛型是保存的状态的类型。
     *
     */
    public static class MyOperatorState implements MapFunction<SensorReading,Integer>, ListCheckpointed<Integer> {
        private Integer count=0; //定义状态
        @Override
        public Integer map(SensorReading sen) throws Exception {
            return count++; //每次状态加一
        }

        //保存状态的方法
        @Override
        public List<Integer> snapshotState(long checkpointId, long timestamp) throws Exception {
            return Collections.singletonList(count);
        }

        //恢复状态的方法
        @Override
        public void restoreState(List<Integer> list) throws Exception {
            for (Integer num:list) { //应用重启如何恢复状态
                count+=num;
            }
        }
    }

    /**
     * 键控状态的类,必须继承富函数,因为通过getRuntimeContext获取上下文给状态属性赋值。
     * 以下实现的功能,按分组key统计个数,用键控状态中的值状态实现。
     *
     */
    public static class MyKeyedValueState extends RichMapFunction<SensorReading, Integer> {
        private ValueState<Integer> keyCount;  //值状态
//        private ListState<String> listState;  //列表状态
//        private MapState<String,Double> mapState;  //映射状态
//        private ReducingState<SensorReading> reducingState;  //聚合状态
        @Override
        public void open(Configuration parameters) throws Exception {
            keyCount =getRuntimeContext().getState(new ValueStateDescriptor<Integer>("key_count",Integer.class));
//            listState=getRuntimeContext().getListState(new ListStateDescriptor<String>("list_state",String.class));
//            mapState=getRuntimeContext().getMapState(new MapStateDescriptor<String, Double>("map_state",String.class,Double.class));
//            reducingState=getRuntimeContext().getReducingState(new ReducingStateDescriptor<SensorReading>());
        }

        @Override
        public Integer map(SensorReading sen) throws Exception {
//           /*
//            ListState的获取和更新
//             */
//            for(String str:listState.get()){
//                System.out.println(str);
//            }
//            listState.add("java");
//            /*
//            MapState的获取和更新
//             */
//            mapState.get("1");
//            mapState.put("2",12.3);

            /*
             ValueState获取和更新
             */
            Integer count = keyCount.value(); //获取count的值
            if(count!=null)
                keyCount.update(++count); //添加count值
            return count;

        }
    }
}

小案例

package com.atguigu.Dstate;

import com.atguigu.Fbeans.SensorReading;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;

/**
 * 测试温度跳变的一个小案例:
 *      温差超过10度输出报警信息:id、当前温度、前一次温度
 */
public class BKeyedStateTest {
    public static void main(String[] args) throws Exception {
        //创建环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        //读取数据并包装成pojo
        DataStreamSource<String> inputStream = env. socketTextStream("localhost",7777);
        DataStream<SensorReading> mapStream = inputStream.map(line -> {
            String[] splits = line.split(",");
            return new SensorReading(new String(splits[0]), new Long(splits[1]), new Double(splits[2]));
        });
        SingleOutputStreamOperator<Tuple3<String, Double, Double>> result = mapStream.keyBy(SensorReading::getId)
                .flatMap(new KeyedStateFunction(10.0));
        //输出执行
        result.print("测试温度跳变的一个小案例");
        env.execute("测试温度跳变的一个小案例");
    }

    public static class KeyedStateFunction extends RichFlatMapFunction<SensorReading, Tuple3<String,Double,Double>>{
        private Double threshold; //温度跳变的阈值
        private ValueState<Double> valueState; //保存上一次的温度值

        public KeyedStateFunction(Double threshold){
            this.threshold=threshold;
        }

        @Override
        public void open(Configuration parameters) throws Exception {
            valueState=getRuntimeContext().getState(new ValueStateDescriptor<Double>("温度监控",Double.class));
        }

        @Override
        public void flatMap(SensorReading sen, Collector<Tuple3<String, Double, Double>> collector) throws Exception {
            Double value = valueState.value();//获取上一次温度值
            if(value!=null){ //判断两次的温度差值
                Double diff=Math.abs(sen.getTemperature()-value);
                if(diff > threshold){
                    collector.collect(new Tuple3<>(sen.getId(),sen.getTemperature(),value));
                }
            }
            //将当前温度值加入状态
            valueState.update(sen.getTemperature());
        }

        @Override
        public void close() throws Exception {
            valueState.clear();//清理状态
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二百四十九先森

你的打赏是我努力的最大动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值