• Flink State 和 Fault Tolerance


    什么是状态?

    所谓的状态,其实指的是 Flink 程序的中间计算结果。Flink 支持了不同类型的状态,并且针对状态的持久化还提供了专门的机制和状态管理器。

    flink状态的应用,比如:

    • When an application searches for certain event patterns, the state will store the sequence of events encountered so far. --复杂事件处理获取符合某一特定时间规则的事件

    • When aggregating events per minute/hour/day, the state holds the pending aggregates. --聚合计算中

    • When training a machine learning model over a stream of data points, the state holds the current version of the model parameters. --机器学习的模型训练

    • When historic data needs to be managed, the state allows efficient access to events that occurred in the past.--使用历史的数据进行计算

    当我们在使用state的时候,应该先熟悉一下flink的state backends,state backends指定了state应该怎么保存以及保存到哪里。(state可以保存到jvm 的堆内存中也可以保存到堆外内存。当然也可以借助第三方存储,例如 Flink 已经实现的对 RocksDB 支持)

     

    状态的类型

    1. Keyed State

    基于KeyedStream使用,Keyed State 是经过分区后的流上状态,每个 Key 都有自己的状态并且只有指定的 key 才能访问和更新自己对应的状态。

    2. Operator State (or non-keyed state)

    Operator State 可以用在所有算子上,每个算子子任务或者说每个算子实例共享一个状态,流入这个算子子任务的数据可以访问和更新这个状态。每个算子子任务上的数据共享自己的状态。Kafka Connector 是一个flink中使用Operator State的很好的例子,每个Kafka consumer都包含了topic partitions和offsets作为其Operator State

    Operator State 的实际应用场景不如 Keyed State 多,一般来说它会被用在 Source 或 Sink 等算子上,用来保存流入数据的偏移量或对输出数据做缓存,以保证 Flink 应用的 Exactly-Once 语义。

    Keyed State和Operator State可以以两种形式存在:分别是Raw State和Managed State

     

    代码示例

        public static void main(String[] args) throws Exception {
    ​
            final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
            env.fromElements(Tuple2.of(1L, 3L), Tuple2.of(1L, 5L), Tuple2.of(1L, 7L), Tuple2.of(1L, 4L), Tuple2.of(1L, 2L))
                    .keyBy(0)
                    .flatMap(new CountWindowAverage())
                    .print();
    ​
            env.execute("submit job");
    ​
        }
    ​
        public static class CountWindowAverage extends RichFlatMapFunction<Tuple2<Long, Long>, Tuple2<Long, Long>> {
            /**
             * The ValueState handle. The first field is the count, the second field a running sum.
             */
            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;
                // access the state value
                if (sum.value() == null) {
                    currentSum = Tuple2.of(0L, 0L);
                } else {
                    currentSum = sum.value();
                }
                // update the count
                currentSum.f0 += 1;
                // add the second field of the input value
                currentSum.f1 += input.f1;
                // update the state
                sum.update(currentSum);
                // if the count reaches 2, emit the average and clear the state
                if (currentSum.f0 >= 2) {
                    out.collect(new Tuple2<>(input.f0, currentSum.f1 / currentSum.f0));
                    sum.clear();
                }
            }
    ​
            @Override
            public void open(Configuration config) {
                ValueStateDescriptor<Tuple2<Long, Long>> descriptor =
                        new ValueStateDescriptor<>(
                                "average", // the state name
                                TypeInformation.of(new TypeHint<Tuple2<Long, Long>>() {
                                }));// type information
                sum = getRuntimeContext().getState(descriptor);
            }
        }

    我们这里的输出条件为,每当第一个元素的和达到二,就把第二个元素的和与第一个元素的和相除,最后输出。我们直接运行,在控制台可以看到结果:

    6> (1,4) 6> (1,5)

     

    状态如何保存及恢复

     Checkpoint 

    Flink 状态保存主要依靠 Checkpoint 机制,Checkpoint 会定时制作分布式快照,对程序中的状态进行备份。分布式快照是如何实现的可以参考入门一的内容,这里就不在阐述分布式快照具体是如何实现的。分布式快照 Checkpoint 完成后,当作业发生故障了如何去恢复?假如作业分布跑在 3 台机器上,其中一台挂了。这个时候需要把进程或者线程移到 active 的 2 台机器上,此时还需要将整个作业的所有 Task 都回滚到最后一次成功 Checkpoint 中的状态,然后从该点开始继续处理。如果要从 Checkpoint 恢复,必要条件是数据源需要支持数据重新发送。Checkpoint 恢复后, Flink 提供两种一致性语义,一种是恰好一次,一种是至少一次。在做 Checkpoint 时,可根据 Barries 对齐来判断是恰好一次还是至少一次,如果对齐,则为恰好一次,否则没有对齐即为至少一次。如果作业是单线程处理,也就是说 Barries 是不需要对齐的;如果只有一个 Checkpoint 在做,不管什么时候从 Checkpoint 恢复,都会恢复到刚才的状态;如果有多个节点,假如一个数据的 Barries 到了,另一个 Barries 还没有来,内存中的状态如果已经存储。那么这 2 个流是不对齐的,恢复的时候其中一个流可能会有重复。Checkpoint 通过代码的实现方法如下:

    • 首先从作业的运行环境 env.enableCheckpointing 传入 1000,意思是做 2 个 Checkpoint 的事件间隔为 1 秒。Checkpoint 做的越频繁,恢复时追数据就会相对减少,同时 Checkpoint 相应的也会有一些 IO 消耗。
    • 接下来是设置 Checkpoint 的 model,即设置了 Exactly_Once 语义,并且需要 Barries 对齐,这样可以保证消息不会丢失也不会重复。
    • setMinPauseBetweenCheckpoints 是 2 个 Checkpoint 之间最少是要等 500ms,也就是刚做完一个 Checkpoint。比如某个 Checkpoint 做了 700ms,按照原则过 300ms 应该是做下一个 Checkpoint,因为设置了 1000ms 做一次 Checkpoint 的,但是中间的等待时间比较短,不足 500ms 了,需要多等 200ms,因此以这样的方式防止 Checkpoint 太过于频繁而导致业务处理的速度下降。
    • setCheckpointTimeout 表示做 Checkpoint 多久超时,如果 Checkpoint 在 1min 之内尚未完成,说明 Checkpoint 超时失败。
      setMaxConcurrentCheckpoints 表示同时有多少个 Checkpoint 在做快照,这个可以根据具体需求去做设置。
    • enableExternalizedCheckpoints 表示下 Cancel 时是否需要保留当前的 Checkpoint,默认 Checkpoint 会在整个作业 Cancel 时被删除。Checkpoint 是作业级别的保存点。


    上面讲过,除了故障恢复之外,还需要可以手动去调整并发重新分配这些状态。手动调整并发,必须要重启作业并会提示 Checkpoint 已经不存在,那么作业如何恢复数据?一方面 Flink 在 Cancel 时允许在外部介质保留 Checkpoint ;另一方面,Flink 还有另外一个机制是 SavePoint。

    SavePoint

    Savepoint 与 Checkpoint 类似,同样是把状态存储到外部介质。当作业失败时,可以从外部恢复。Savepoint 与 Checkpoint 有什么区别呢?

    • 从触发管理方式来讲,Checkpoint 由 Flink 自动触发并管理,而 Savepoint 由用户手动触发并人肉管理;
    • 从用途来讲,Checkpoint 在 Task 发生异常时快速恢复,例如网络抖动或超时异常,而 Savepoint 有计划地进行备份,使作业能停止后再恢复,例如修改代码、调整并发;
    • 最后从特点来讲,Checkpoint 比较轻量级,作业出现问题会自动从故障中恢复,在作业停止后默认清除;而 Savepoint 比较持久,以标准格式存储,允许代码或配置发生改变,恢复需要启动作业手动指定一个路径恢复。

    可选的状态存储方式

    Checkpoint 的存储,第一种是内存存储,即 MemoryStateBackend,构造方法是设置最大的 StateSize,选择是否做异步快照,这种存储状态本身存储在 TaskManager 节点也就是执行节点内存中的,因为内存有容量限制,所以单个 State maxStateSize 默认 5 M,且需要注意 maxStateSize <= akka.framesize 默认 10 M。Checkpoint 存储在 JobManager 内存中,因此总大小不超过 JobManager 的内存。推荐使用的场景为:本地测试、几乎无状态的作业,比如 ETL、JobManager 不容易挂,或挂掉影响不大的情况。不推荐在生产场景使用。

    另一种就是在文件系统上的 FsStateBackend ,构建方法是需要传一个文件路径和是否异步快照。State 依然在 TaskManager 内存中,但不会像 MemoryStateBackend 有 5 M 的设置上限,Checkpoint 存储在外部文件系统(本地或 HDFS),打破了总大小 Jobmanager 内存的限制。容量限制上,单 TaskManager 上 State 总量不超过它的内存,总大小不超过配置的文件系统容量。推荐使用的场景、常规使用状态的作业、例如分钟级窗口聚合或 join、需要开启 HA 的作业。

    还有一种存储为 RocksDBStateBackend ,RocksDB 是一个 key/value 的内存存储系统,和其他的 key/value 一样,先将状态放到内存中,如果内存快满时,则写入到磁盘中,但需要注意 RocksDB 不支持同步的 Checkpoint,构造方法中没有同步快照这个选项。不过 RocksDB 支持增量的 Checkpoint,也是目前唯一增量 Checkpoint 的 Backend,意味着每次用户不需要将所有状态都写进去,将增量的改变的状态写进去即可。它的 Checkpoint 存储在外部文件系统(本地或 HDFS),其容量限制只要单个 TaskManager 上 State 总量不超过它的内存 + 磁盘,单 Key 最大 2G,总大小不超过配置的文件系统容量即可。推荐使用的场景为:超大状态的作业,例如天级窗口聚合、需要开启 HA 的作业、最好是对状态读写性能要求不高的作业。

    配置State Backend

    默认的state backend是jobmanager(也就是MemoryStateBackend),想自己指定的话可以通过以下方式

    1.修改flink-conf.yaml(对应全局任务)

    在配置文件里通过指定state.backend的值来选择使用哪种state backend,可配的值:

    • jobmanager 表示使用MemoryStateBackend

    • filesystem 表示使用FsStateBackend

    • rocksdb 表示使用RocksDBStateBackend

    另外还有个state.checkpoints.dir,用来指定所有的backends在写checkpoint数据和元数据文件的路径

    比如在配置文件里可以这样:

    # The backend that will be used to store operator state checkpoints
    state.backend: filesystem
    ​
    # Directory for storing checkpoints
    state.checkpoints.dir: hdfs://namenode:40010/flink/checkpoints

    2.在代码中指定(对应单个job)

    比如指定使用FsStateBackend

    StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    env.setStateBackend(new FsStateBackend("hdfs://namenode:40010/flink/checkpoints"));

    如果要使用RocksDBStateBackend,需要在添加以下依赖:

    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-statebackend-rocksdb_2.11</artifactId>
        <version>1.10.0</version>
        <scope>provided</scope>
    </dependency>

    在使用RocksDBStateBackend时候,建议使用增量checkpoints,配置方式:

    1. 在flink-conf.yaml中配置state.backend.incremental: true
    2. 在代码中
    //使用RocksDBStateBackend
    StateBackend rockdbBackend = new RocksDBStateBackend("hdfs://namenode:40010/flink/checkpoints", true);
    env.setStateBackend(rockdbBackend);

    参考:Flink chian社区

    参考:Flink 官网

  • 相关阅读:
    6月23号 java方法 什么是方法?
    6月21号 Java流程控制 练习
    6月21号 Java流程控制 break continue
    6月21号 Java流程控制 增强for
    6月19号 Java流程控制 循环结构for练习
    6月18号 Java流程控制 循环结构for
    qbot
    clion 2019.2
    raptor
    字符串练习
  • 原文地址:https://www.cnblogs.com/zz-ksw/p/12973639.html
Copyright © 2020-2023  润新知