flink学习笔记5-flink状态和检查点

flink状态

要实现有且只有一次或者至少一次处理语义,需要保存相关的中间状态数据,在故障恢复时进行还原故障前系统的运行状态。在flink中,定义了操作状态和分组状态两种状态,且定义了检查点机制来定时触发检查点,触发检查点会将flink状态保存到statebackends中,所谓statebackends就是定义触发检查点后,将状态数据保存到哪里,默认是保存到jobmanager的内存中。

状态分类

  • 操作状态(operator state):只能运行在非keyedstream流中,如kafka作为source时,每个分区都对应一个操作状态,可以使用操作状态存储偏移量
  • 分组状态(keyed state):只能在KeyedStream流中的算子中使用

分组状态keyed state

keyed state分类

  1. ValueState:值状态,具有相同的key时,获取到的值状态是一样的。可以理解成key由flink管理,而key对应的值由程序员自己管理。x.update(T)设置值,T t=x.value()获取值
  2. ListState:集合状态,和值状态一样,唯一的区别是,集合状态中的数据是多个而值状态中的数据是一个。add(T) 、 addAll(List)添加值,get()获取值,update(List)更新值
  3. ReducingState:在ReduceFunction函数中使用,将数据进行聚合并将聚合的结果保存下来,类似ListState,用add(T)添加数据
  4. AggregatingState:在AggregatingState函数中使用,将数据IN进行聚合并保存下来,类似ListState,用add(IN)添加数据
  5. FoldingState:过时,用AggregatingState
  6. MapState:保存一个map类型的数据, put(UK, UV) or putAll(Map)添加数据,get(UK)、entries(), keys() 、 values()获取数据

上述所有的状态数据都可以通过clear()方法进行清空。

keyed state,可以理解成一个map,键为keyBy的值,由flink管理,值可以是上述分类中的任意一种

keyed state实例代码

/**
* 1. 需要继承RichFlatMapFunction而不是MapFunction,因为需要中间状态初始化
*/
public class CountWindowAverage extends RichFlatMapFunction<Tuple2<Long, Long>, Tuple2<Long, Long>> {

    /**
     * 定义成员变量为中间状态,可以是ValueState、ListState等,注意需要使用transient修饰
     */
    private transient ValueState<Tuple2<Long, Long>> sum;

    @Override
    public void flatMap(Tuple2<Long, Long> input, Collector<Tuple2<Long, Long>> out) throws Exception {
        //获取中间状态的值
        Tuple2<Long, Long> currentSum = sum.value();
        
        //业务逻辑处理
        currentSum.f0 += 1;
        currentSum.f1 += input.f1;
        //更新中间状态的值
        sum.update(currentSum);
        
        if (currentSum.f0 >= 2) {
            out.collect(new Tuple2<>(input.f0, currentSum.f1 / currentSum.f0));
            //清空中间状态的值
            sum.clear();
        }
    }

/**
* 中间状态初始化
* @param config
*/
    @Override
    public void open(Configuration config) {
        //定义中间中台描述,
        ValueStateDescriptor<Tuple2<Long, Long>> descriptor =
                new ValueStateDescriptor<>(
                        "average", // 状态名称,需要在flink中唯一
                        TypeInformation.of(new TypeHint<Tuple2<Long, Long>>() {})); // 状态的类型
        //可以理解成在flink中,有一个全局map,键为average,值为一个局部map,局部map的key为keyby的值,局部map的值为Tuple2
        //Map>> map,
        sum = getRuntimeContext().getState(descriptor);
    }
}

操作状态 operator state

为了使用操作状态,可以通过实现如下接口:

  1. CheckpointedFunction:
  2. ListCheckpointed

实现CheckpointedFunction的操作状态代码示例

//每次检查点执行时,都要调用这个方法
void snapshotState(FunctionSnapshotContext context) throws Exception;
//系统初始化时或者进行故障恢复时,都要调用这个方法
void initializeState(FunctionInitializationContext context) throws Exception;

代码示例:

//实现CheckpointedFunction接口,并重写snapshotState和initializeState方法       
public class BufferingSink
        implements SinkFunction<Tuple2<String, Integer>>,
                   CheckpointedFunction {

    private final int threshold;
    //operator state类型的中间状态
    private transient ListState<Tuple2<String, Integer>> checkpointedState;

    private List<Tuple2<String, Integer>> bufferedElements;

    public BufferingSink(int threshold) {
        this.threshold = threshold;
        this.bufferedElements = new ArrayList<>();
    }
    //当数据到来时,先加入集合中,如果数据条数达到一个阈值时,将集合中的数据保存到sink中,并清空集合
    //类似countwindow的功能
    @Override
    public void invoke(Tuple2<String, Integer> value, Context contex) throws Exception {
        bufferedElements.add(value);
        if (bufferedElements.size() == threshold) {
            for (Tuple2<String, Integer> element: bufferedElements) {
                // send it to the sink
            }
            bufferedElements.clear();
        }
    }

    //每次检查点执行时,都要调用这个方法
    @Override
    public void snapshotState(FunctionSnapshotContext context) throws Exception {
        //将中间状态数据清空
        checkpointedState.clear();
        //从集合中添加数据到中间状态,这个方法执行完后,会将中间状态数据保存到State Backends
        for (Tuple2<String, Integer> element : bufferedElements) {
            checkpointedState.add(element);
        }
    }

    //系统初始化时或者进行故障恢复时,都要调用这个方法
    @Override
    public void initializeState(FunctionInitializationContext context) throws Exception {
        //operator state中间状态描述器
        ListStateDescriptor<Tuple2<String, Integer>> descriptor =
            new ListStateDescriptor<>(
                "buffered-elements",
                TypeInformation.of(new TypeHint<Tuple2<String, Integer>>() {}));
        //初始化operator state中间状态
        checkpointedState = context.getOperatorStateStore().getListState(descriptor);
        //如果是故障恢复时,需要将中间状态的数据初始化到集合中
        if (context.isRestored()) {
            for (Tuple2<String, Integer> element : checkpointedState.get()) {
                bufferedElements.add(element);
            }
        }
    }
}

可以参考kafka的source,也是基于CheckpointedFunction实现的

实现ListCheckpointed的操作状态代码示例

没太看懂

检查点知识

何为检查点:

就是将程序运行过程中的状态数据以快照的形式周期性的保存起来(后一次状态数据会覆盖前一次的状态数据),用于程序重启时恢复相关中间状态,每一次检查点的执行都会触发状态数据的保存。

检查点数据保存到哪里呢?默认保存到job manager的内存中,也可以在集群中进行配置,可以保存到文件系统中、HDFS中。

检查点执行流程:

  1. jobmanager根据系统设置的检查点执行周期触发检查点操作指令,并将操作指令发送给对应系统的taskmanager
  2. taskmanager收到检查点操作指令后,将本taskmanager负责的系统对应的中间状态保存到statebackends中,执行完成后向jobmanager反馈检查点操作执行结果
  3. jobmanager收到taskmanager关于坚持点操作指令结果后,更新检查点执行记录,如果在一定的时间还未收到检查点执行结果,那么这次检查点将被丢弃。
  4. 当taskmanager负责的任务或整个job重启时,会去statebackends中获取中间状态数据,用以保证有且只有一次或者最少一次语义

注意:
中间状态数据只有在开启检查点后才能在检查点触发时,将中间状态数据保存到statebackends中,如果没有开启检查点,那么中间状态数据只存储在taskmanager的jvm内存中,和一般的成员属性类似,不能用于故障恢复

检查点配置

public class Test{
    public static void main(String[] args){
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
            //设置statebackends为HDFS
            env.setStateBackend(new FsStateBackend("hdfs://namenode:40010/flink/checkpoints"));
            // 开启检查点,并没秒执行一次
            env.enableCheckpointing(1000);
    
            // 设置精确一次模式,也可以设置最少一次模式
            env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
    
            //设置检查点之间最短时间间隔
            env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
    
            //检查点操作必须在多少毫秒内完成,否则这次的检查点将被丢弃
            env.getCheckpointConfig().setCheckpointTimeout(60000);
    
            // 设置同时允许运行多少个检查点操作,设置setMinPauseBetweenCheckpoints后不要再设置此项
            env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
    
            // 设置系统重启,是否删除检查点数据,如果设置DELETE_ON_CANCELLATION,则系统重启后,检查点数据将被删除,
            // 如果设置RETAIN_ON_CANCELLATION,则系统重启后,会读取检查点的数据
            env.getCheckpointConfig().enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
    
            //故障恢复时,是否从最新的检查点进行恢复数据
            env.getCheckpointConfig().setPreferCheckpointForRecovery(true);
        }
}

更多检查点相关的配置可以配置在flink集群的conf/flink-conf.yaml配置文件中

statebackends

statebackends是检查点存储中间状态数据的地方,一般保存如下两种类型:

  1. MemoryStateBackend:将数据存储在jobmanager的内存中,env.setStateBackend(new MemoryStateBackend(MAX_MEM_STATE_SIZE, false));
  2. FsStateBackend:将数据存储在文件系统中,可以是远程共享文件也可以是HDFS,env.setStateBackend(new FsStateBackend(“hdfs://namenode:40010/flink/checkpoints”));
  3. RocksDBStateBackend:不太明白

在conf/flink-conf.yaml配置文件中配置statebackends如下:

state.backend: filesystem
state.checkpoints.dir: hdfs://namenode:40010/flink/checkpoints

你可能感兴趣的:(flink学习笔记,大数据,flink)