Flink - DataStream Checkpoint

557 阅读9分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

Flink 的 4 大基石为:Time , Window, State , Checkpoint.之前的文章已经学习了前面 3 个内容,接下来主要学习 Checkpoint 的相关内容。Checkpoint 与 State 有密不可分的关系,下面将从 Checkpoint 与 State 的关系和区别开始,再学习 Checkpoint 的执行流程,存储介质,配置管理等。然后再学习 Flink 的自动重启机制,还有页面手动重启。

Checkpoint 与 State 的关系和区别

State:

  • state 维护/存储的是某一个 operator 运行的状态/历史值。
  • 存储在内存中
  • state 可以被记录,在失败的情况下数据还可以还原

Checkpoint:

  • 某一时刻,Flink 中所有的 Operator 的当前 State 的全局快照
  • 一般存储在磁盘中

可以理解为 Checkpoint 是把 State 数据定时持久化存储了,state 其实就是 Checkpoint 所做的主要持久化备份的主要数据。

Checkpoint 执行流程

image.png barrier 可以理解为执行 checkpoint 的信号。

  • 第一步,Checkpoint Coordinator 向所有 source 节点 trigger Checkpoint。SourceOperator 接收到 barrier 之后,会暂停当前的操作(暂停的时间很短,因为后面的写快照操作时异步的),并制作 state 快照,然后将自己的快照保存在指定的介质中。 image.png

  • 第二步,source 节点向下游广播 barrier,这个 barrier 就是实现 Chandy-Lamport 分布式快照算法的核心,下游的 task 只有收到所有 input 的 barrier 才 会执行相应的 Checkpoint。 image.png

  • 第三步,当 task 完成 state 备份后,会将备份数据的地址(state handle) 通知给 Checkpoint coordinator。 image.png

  • 第四步,下游的 sink 节点收集齐上游两个 input 的 barrier 之后,会执行本地快照,这里特地展示了 RocksDB incremental Checkpoint 的流程,首 先 RocksDB 会全量刷数据到磁盘上(红色大三角表示),然后 Flink 框架会从中选择没有上传的文件进行持久化备份(紫色小三角)。 image.png

  • 同样的,sink 节点在完成自己的 Checkpoint 之后,会将 state handle 返 回通知 Coordinator。 image.png

  • 最后,当 Checkpoint coordinator 收集齐所有 task 的 state handle,就认为这一次的 Checkpoint 全局完成了,向持久化存储中再备份一个 Checkpoint meta 文件。 image.png

从 source 执行 checkpiont 会短暂暂停当前操作,得出经验,checkpoint 每次触发的事件应该是秒级的,比如 1秒,3秒, 5 秒等。如果是毫秒级的操作,比如 200 ms ,有可能会影响运算。

Checkpoint 状态存储后端/存储介质

MemoryStateBackend:

  • 存储方式:
  1. state - TaskManager 内存
  2. checkpoint - JobManager 内存
  • 容量限制:
  1. 单个 State maxStateSize 默认 5M
  2. maxStateSize <= akka.framesize 默认 10M
  3. 总大小不超过 JobManager 的内存 只适用于本地测试,不适合生产使用

FsStateBackend:

  • 存储方式:
  1. state - TaskManager 内存
  2. checkpoint - 外部文件系统(本地或者 HDFS)
  • 容量限制:
  1. 单 TaskManager 上的 State 总量不超过它的内存
  2. 总大小不超过配置的文件系统容量

常规使用状态的作业,例如分钟级别窗口聚合/join;需要开启 HA 的作业。可以在生产场景使用。 所以通常使用 FsStateBackend 来做存储介质。

RocksDBStateBackend:

  • 存储方式:
  1. state - TaskManager 上的 KV 数据库(实际使用内存 + 磁盘)
  2. checkpoint - 外部文件系统(本地或者 HDFS)
  • 容量限制:
  1. 单 TaskManager 上的 State 总量不超过它的内存 + 磁盘
  2. 单个 Key 最大 2 G
  3. 总大小不超过配置的文件系统容量

适合超大状态作业的场景,对状态读写性能要求不高的作业。可以在生产场景使用。 使用时需要在程序中添加依赖。

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-statebackend-rocksdb_2.11</artifactId>
    <version>1.13.2</version>
</dependency>

新版本后,发生了一些更改:

image.png

配置说明

配置可以在官网中找到:

image.png

package com.learn.flink.checkpoint;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.runtime.state.filesystem.FsStateBackend;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.CheckpointConfig;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * 演示 Flink - checkpoint 相关配置
 */
public class CheckpointDemo1 {

    public static void main(String[] args) throws Exception {
        //0:env
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);
        //1: source
        // ################## 类型一: 必须参数 ###############
        // 设置 checkpoint 的时间间隔为 1000 毫秒/其实就是每隔 1000 ms 发一次 barrier
        env.enableCheckpointing(1000);
        // 设置存储位置
        env.setStateBackend(new FsStateBackend("file:///D:\workspace\TEST\learn-flink\data\ckp"));
//        env.getCheckpointConfig().setCheckpointStorage("hdfs://192.168.98.101:9000/flink/flink-checkpoints");
        // ################## 类型二: 建议参数 ###############
        // 设置两个 checkpoint 之间的最小等待时间(为了避免每个 1000 ms 做一次 checkpoint 的时候,前一次太慢和后一个重叠在一起)
        env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
        // 设置容忍检查点失败的次数,默认是 0 ,即不容忍,只要有一个 checkpoint 失败,就认为任务失败
        env.getCheckpointConfig().setTolerableCheckpointFailureNumber(2);
        // CheckpointConfig.ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION:当外部作用被取消时,删除外部 checkpoint(默认值)
        // CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION:当外部作用被取消时,保留外部 checkpoint
        env.getCheckpointConfig().enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
        // ################## 类型三: 直接使用默认配置即可 ###############
        // checkpoint 执行模式 (默认为 EXACTLY_ONCE)
        env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
        // 设置 checkpoint 超时时间,如果 60 s 内没有完成,则认为该次 checkpoint 失败,则丢弃
        env.getCheckpointConfig().setCheckpointTimeout(60000);
        //设置一次有多少个 checkpoint 可以同时执行,默认为 1
        env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);

        final DataStreamSource<String> ds = env.socketTextStream("node01", 999);
        //2: transformation

        //3: sink
        ds.print();
        //4: execute
        env.execute();

    }
}

可以看到一些配置也有些变化。

状态恢复和重启策略

自动重启

  • 默认重启策略:这个是无限重启的,可以解决小问题,但是可能会隐藏真正的问题。
  • 无重启策略
  • 固定延迟重启策略:开发中使用
  • 失败率重启策略:开发中偶而使用

模拟异常的例子:

package com.learn.flink.checkpoint;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.restartstrategy.RestartStrategies;
import org.apache.flink.api.common.time.Time;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.runtime.state.filesystem.FsStateBackend;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.CheckpointConfig;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;

import java.util.concurrent.TimeUnit;

/**
 * 演示 Flink - checkpoint 相关配置
 */
public class CheckpointDemo2_restart {

    public static void main(String[] args) throws Exception {
        //0:env
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);
        //1: source
        // ################## 类型一: 必须参数 ###############
        // 设置 checkpoint 的时间间隔为 1000 毫秒/其实就是每隔 1000 ms 发一次 barrier
        env.enableCheckpointing(1000);
        // 设置存储位置
        env.setStateBackend(new FsStateBackend("file:///D:\workspace\TEST\learn-flink\data\ckp"));
//        env.getCheckpointConfig().setCheckpointStorage("hdfs://192.168.98.101:9000/flink/flink-checkpoints");
        // ################## 类型二: 建议参数 ###############
        // 设置两个 checkpoint 之间的最小等待时间(为了避免每个 1000 ms 做一次 checkpoint 的时候,前一次太慢和后一个重叠在一起)
        env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
        // 设置容忍检查点失败的次数,默认是 0 ,即不容忍,只要有一个 checkpoint 失败,就认为任务失败
        env.getCheckpointConfig().setTolerableCheckpointFailureNumber(2);
        // CheckpointConfig.ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION:当外部作用被取消时,删除外部 checkpoint(默认值)
        // CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION:当外部作用被取消时,保留外部 checkpoint
        env.getCheckpointConfig().enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
        // ################## 类型三: 直接使用默认配置即可 ###############
        // checkpoint 执行模式 (默认为 EXACTLY_ONCE)
        env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
        // 设置 checkpoint 超时时间,如果 60 s 内没有完成,则认为该次 checkpoint 失败,则丢弃
        env.getCheckpointConfig().setCheckpointTimeout(60000);
        //设置一次有多少个 checkpoint 可以同时执行,默认为 1
        env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
        //默认重启策略:这个是无限重启的,可以解决小问题,但是可能会隐藏真正的问题。
        //如果设置为无重启策略,则抛出异常后,程序停止
        //env.setRestartStrategy(RestartStrategies.noRestart());
        //固定延迟重启,最多重启 3 次,间隔为 5 秒---开发中常使用
//        env.setRestartStrategy(RestartStrategies.fixedDelayRestart(
//                3, //最多重启 3 次
//                Time.of(5, TimeUnit.SECONDS))// 重启的时间间隔
//        );
        // 失败率重启:开发中偶而使用
        env.setRestartStrategy(RestartStrategies.failureRateRestart(
                3, // 每个测量阶段内最大失败次数
                Time.of(5, TimeUnit.SECONDS),// 失败率测量的时间间隔
                Time.of(10, TimeUnit.SECONDS)// 两次连续重启的时间间隔
        ));
        final DataStreamSource<String> linesDS = env.socketTextStream("node01", 999);
        //2: transformation
        // 切割并记录为 1
        final SingleOutputStreamOperator<Tuple2<String, Integer>> wordAndOne = linesDS.flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {
            @Override
            public void flatMap(String value, Collector<Tuple2<String, Integer>> collector) throws Exception {
                final String[] arr = value.split(" ");
                for (String word : arr) {
                    if("bug".equalsIgnoreCase(word)) {
                        System.out.println("bug.........");
                        throw new Exception("bug...");
                    }
                    collector.collect(Tuple2.of(word, 1));
                }
            }
        });

        // 分组
        final KeyedStream<Tuple2<String, Integer>, String> grouped = wordAndOne.keyBy(t -> t.f0);

        //聚合
        final SingleOutputStreamOperator<Tuple2<String, Integer>> result = grouped.sum(1);

        //3: sink
        result.print();
        //4: execute
        env.execute();

    }
}

在之前的代码中添加异常信息:

image.png

  • 默认无重启策略:当输入 bug 单词的时候,会抛出异常并打印,然后继续输入单词,会从历史记录的计数开始继续累加。

image.png

说明在配置 checkpoint 后,Flink 包含默认重启策略,并且可以一直无限重启。那样这种默认重启策略可以解决小问题,但是可能会隐藏真正的问题。

注意重启策略需要配置在 source 之前,并且只有在配置了 checkpoint 后才是有效的

  • 设置无重启策略,增加配置
env.setRestartStrategy(RestartStrategies.noRestart());

则结果为抛出异常,任务停止,没有再重启。 image.png

  • 设置固定延迟重启策略,增加配置
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(
        3, //最多重启 3 次
        Time.of(5, TimeUnit.SECONDS))// 重启的时间间隔
);

可以设置重启的最大次数,并设置间隔时间。每次重启会间隔配置的时间,重启次数到达最大值后,不再重启,任务停止。这种方式在开发中是经常使用的,比如遇到一些网络的问题,通过这种方式可以很好的处理。 image.png

  • 设置失败率重启策略,增加配置
env.setRestartStrategy(RestartStrategies.failureRateRestart(
        3, // 每个测量阶段内最大失败次数
        Time.of(5, TimeUnit.SECONDS),// 失败率测量的时间间隔
        Time.of(10, TimeUnit.SECONDS)// 两次连续重启的时间间隔
));

可以设置在每个测量阶段内的最大失败次数,并设置失败率测量的时间间隔,并设置两次重启的时间间隔。这种方式在开发种偶而使用。有了时间间隔不好控制时间,不常使用。 image.png

手动重启

手动重启其实就是使用 Flink 的 web 页面,上传任务,然后重启任务,在重启的时候可以指定checkpoint进行恢复。

示例:单词计数,输出到 kafka 种

package com.example.flink;

import org.apache.commons.lang3.SystemUtils;
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.runtime.state.filesystem.FsStateBackend;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.CheckpointConfig;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer;
import org.apache.flink.util.Collector;

import java.util.Properties;

public class WordCountDemo3 {
    public static void main(String[] args) throws Exception {

        // TODO 0: env
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        // ################## 类型一: 必须参数 ###############
        // 设置 checkpoint 的时间间隔为 1000 毫秒/其实就是每隔 1000 ms 发一次 barrier
        env.enableCheckpointing(1000);
        // 设置存储位置
        if (SystemUtils.IS_OS_WINDOWS) {
            env.setStateBackend(new FsStateBackend("file:///D:\workspace\TEST\learn-flink\data\ckp"));
        } else {
            env.setStateBackend(new FsStateBackend("hdfs://node01:9000/flink/checkpoints-test"));
        }
        // ################## 类型二: 建议参数 ###############
        // 设置两个 checkpoint 之间的最小等待时间(为了避免每个 1000 ms 做一次 checkpoint 的时候,前一次太慢和后一个重叠在一起)
        env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
        // 设置容忍检查点失败的次数,默认是 0 ,即不容忍,只要有一个 checkpoint 失败,就认为任务失败
        env.getCheckpointConfig().setTolerableCheckpointFailureNumber(2);
        // CheckpointConfig.ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION:当外部作用被取消时,删除外部 checkpoint(默认值)
        // CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION:当外部作用被取消时,保留外部 checkpoint
        env.getCheckpointConfig().enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
        // ################## 类型三: 直接使用默认配置即可 ###############
        // checkpoint 执行模式 (默认为 EXACTLY_ONCE)
        env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
        // 设置 checkpoint 超时时间,如果 60 s 内没有完成,则认为该次 checkpoint 失败,则丢弃
        env.getCheckpointConfig().setCheckpointTimeout(60000);
        //设置一次有多少个 checkpoint 可以同时执行,默认为 1
        env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);

        // TODO 1: source
        DataStream<String> text = env.socketTextStream("node01", 9999);
        // TODO 2: transformation
        DataStream<String> counts = text
                .flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {
                    @Override
                    public void flatMap(String value, Collector<Tuple2<String, Integer>> out) throws Exception {
                        // normalize and split the line
                        String[] tokens = value.toLowerCase().split(" ");

                        // emit the pairs
                        for (String token : tokens) {
                            if (token.length() > 0) {
                                out.collect(new Tuple2<>(token, 1));
                            }
                        }
                    }
                })
                .keyBy(value -> value.f0)
                .sum(1)
                .map(new MapFunction<Tuple2<String, Integer>, String>() {
                    @Override
                    public String map(Tuple2<String, Integer> value) throws Exception {
                        return value.f0 + ":" + value.f1;
                    }
                });
        // TODO 3: sink
        Properties propsProducer = new Properties();
        propsProducer.setProperty("bootstrap.servers", "node01:9092");
        final FlinkKafkaProducer<String> kafkaSink = new FlinkKafkaProducer<>("flink_kafka2", new SimpleStringSchema(), propsProducer);
        counts.addSink(kafkaSink);
        // TODO 4: execute
        env.execute("WordCountDemo3");
    }
}

使用 WEB 页面提交的任务时遇到一个坑,就是上传的 jar 包总是报错:

WARNING: All illegal access operations will be denied in a future release
java.lang.NoClassDefFoundError: org/apache/flink/streaming/connectors/kafka/FlinkKafkaProducer
	at com.example.flink.WordCountDemo3.main(WordCountDemo3.java:51)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.apache.flink.client.program.PackagedProgram.callMainMethod(PackagedProgram.java:355)
	at org.apache.flink.client.program.PackagedProgram.invokeInteractiveModeForExecution(PackagedProgram.java:222)
	at org.apache.flink.client.ClientUtils.executeProgram(ClientUtils.java:114)
	at org.apache.flink.client.cli.CliFrontend.executeProgram(CliFrontend.java:812)
	at org.apache.flink.client.cli.CliFrontend.run(CliFrontend.java:246)
	at org.apache.flink.client.cli.CliFrontend.parseAndRun(CliFrontend.java:1054)
	at org.apache.flink.client.cli.CliFrontend.lambda$main$10(CliFrontend.java:1132)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/javax.security.auth.Subject.doAs(Subject.java:423)
	at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1754)
	at org.apache.flink.runtime.security.contexts.HadoopSecurityContext.runSecured(HadoopSecurityContext.java:41)
	at org.apache.flink.client.cli.CliFrontend.main(CliFrontend.java:1132)
Caused by: java.lang.ClassNotFoundException: org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
	at org.apache.flink.util.FlinkUserCodeClassLoader.loadClassWithoutExceptionHandling(FlinkUserCodeClassLoader.java:64)
	at org.apache.flink.util.ChildFirstClassLoader.loadClassWithoutExceptionHandling(ChildFirstClassLoader.java:65)
	at org.apache.flink.util.FlinkUserCodeClassLoader.loadClass(FlinkUserCodeClassLoader.java:48)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
	... 17 more

这里就是缺少 jar 包,需要把依赖的 jar 包都打入才可以。在 pom.xml 文件种增加配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version><!--$NO-MVN-MAN-VER$-->
            <executions>
                <!-- Default Execution -->
                <execution>
                    <id>default</id>
                    <phase>package</phase>
                    <goals>
                        <goal>test-jar</goal>
                    </goals>
                </execution>
                <!--WordCountDemo3-->
                <execution>
                    <id>WordCountDemo3</id>
                    <phase>package</phase>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                    <configuration>
                        <classifier>WordCountDemo3</classifier>
                        <archive>
                            <manifestEntries>
                                <program-class>com.example.flink.WordCountDemo3</program-class>
                            </manifestEntries>
                        </archive>
                        <includes>
                            <include>com/example/flink/WordCountDemo3.class</include>
                            <include>com/example/flink/WordCountDemo3$*.class</include>
                            <include>META-INF/LICENSE</include>
                            <include>META-INF/NOTICE</include>
                        </includes>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <!--To build an application JAR that contains all dependencies required for declared connectors and libraries-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.1.1</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <artifactSet>
                            <excludes>
                                <exclude>com.google.code.findbugs:jsr305</exclude>
                                <exclude>org.slf4j:*</exclude>
                                <exclude>log4j:*</exclude>
                            </excludes>
                        </artifactSet>
                        <filters>
                            <filter>
                                <!-- Do not copy the signatures in the META-INF folder.
                                Otherwise, this might cause SecurityExceptions when using the JAR. -->
                                <artifact>*:*</artifact>
                                <excludes>
                                    <exclude>META-INF/*.SF</exclude>
                                    <exclude>META-INF/*.DSA</exclude>
                                    <exclude>META-INF/*.RSA</exclude>
                                </excludes>
                            </filter>
                        </filters>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.example.flink.WordCountDemo3</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>

    </plugins>
</build>

添加这个配置后,可以提交成功并正常执行任务。

  • 打包,注意添加上面的 pom 配置

  • 通过网页上传任务 image.png

  • 跳转到任务运行页面 image.png

  • 手动取消任务 image.png

  • 查看 HDFS 种保存的 checkpoint image.png

  • 再次提交任务,并指定 savepointhdfs://node01:9000/flink/checkpoints-test/993a9c7f217176fc56f3533c37647e9a/chk-81

  • 任务执行后会从指定的 savepoint 的位置开始 state 的计算

image.png

从 7 的位置恢复的,再发送数据时,计数开始增加。 image.png