flink1.12.x checkpoint:代码演示案例(封装设置checkpoint方法)

案例

socket 服务器向 Flink 程序发送数据,当数据格式错误时,程序崩溃重启后,仍然能继续运算,状态不丢失

提示:此案例不能用kafka做演示,读取的错误格式数据,程序崩溃重启后,仍然会读取该错误格式数据

格式如下:

1,30

解释:

  • 1:表示传感器
  • 30:表示发送的温度

打印各个传感器的 温度最大值

当输入数据格式错误时,程序崩溃,3秒后会重新启动,从checkpoint恢复后,继续运行,连续错误2次后,程序退出

flink java代码

关键:

  • 每隔 5000 ms 执行一次 checkpoint
package checkpoint;

import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.restartstrategy.RestartStrategies;
import org.apache.flink.api.java.functions.KeySelector;
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.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.CheckpointConfig;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

public class S2报错保存socket {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        setCheckpoint(env);

        DataStreamSource<String> dss = env.socketTextStream("localhost", 9999);

        SingleOutputStreamOperator<Tuple2<String,Double>> soso = dss
                .map(new MapFunction<String,Tuple2<String,Double>>(){

                        @Override
                        public Tuple2<String,Double> map(String s) throws Exception {
                            System.out.println("receive:"+s);
                            String[] arr = s.split(",");
                            double temp = Double.parseDouble(arr[1]);
                            return Tuple2.of(arr[0],temp);
                        }
                    })
                .keyBy(new KeySelector<Tuple2<String,Double>,String>(){

                    @Override
                    public String getKey(Tuple2<String, Double> value) throws Exception {
                        return value.f0;
                    }
                })
                .maxBy(1);

        soso.print("result:");

        env.execute();
    }

    private static void setCheckpoint(StreamExecutionEnvironment env){
        // 必设置,每隔 5000 ms 执行一次checkpoint。
        env.enableCheckpointing(5000);//一般秒级、分钟级
        //设置保存文件路径,生产环境中应该是 HDFS
//      env.setStateBackend(new FsStateBackend("hdfs://hadoop1:8020/"+appName+"/ck/"));
        env.setStateBackend(new FsStateBackend("file:///D:/flink_cp"));
        //env.setStateBackend(new RocksDBStateBackend("file:///D:/bigdata/flink_java/state/RocksDB",true));
        // 设置2个checkpoint之间最少等待时间,这里至少要等到500ms
        env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
        //checkpoint容忍的错误数量,达到此数量,就让该任务失败,默认值是:0。这里容忍3个错误
        env.getCheckpointConfig().setTolerableCheckpointFailureNumber(3);
        //取消任务时,是否清理checkpoint
        // true:取消任务时,会删除外部checkpoint,默认值
        // false:取消任务时,保留外部checkpoint
        //这里设置不删除
        env.getCheckpointConfig().enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
        //设置checkpoint的执行模式:EXACTLY_ONCE,默认
        env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
        // 设置checkpoint的超时时间,如果在60s内尚未完成说明本次checkpoint失败,则丢弃
        env.getCheckpointConfig().setCheckpointTimeout(60000);//这里设置10分钟
        // 设置同一时间有多少个checkpoint可以同时执行。与 env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500); 可能冲突
        //env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);//默认为1
        // 重启策略:程序出现异常时,重启2次,每次延迟3秒中重启,超过2次退出程序
        env.setRestartStrategy(RestartStrategies.fixedDelayRestart(2, 3000));
        /*
         如果保存到HDFS中,需要设置用户,否则调试时,一般会以 windows 用户操作HDFS,会报错没有权限
         - 学习期间,设置用户为root(hadoop超级管理员)
         - 企业开发中,给hadoop添加用户,并设置密码、权限,这里设置此用户
         */
        System.setProperty("HADOOP_USER_NAME", "root");
    }

}

java 测试类

当 flink 程序连接socket后,每间隔1秒发送一个数据

数据格式:

1,11
1,12

最后一条数据格式错误,故意模拟错误

代码

package top.malaoshi.socket;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    public static void main(String[] args) {
        int port = 9999;
        try {
            ServerSocket ss = new ServerSocket(port);
            System.out.println("server is running,port:"+port);
            while(true) {
                Socket client = ss.accept();

                InetAddress inetAddress = client.getInetAddress();
                String clientAddress = inetAddress.getHostAddress();
                int clientPort = client.getPort();
                System.out.println("new client:"+clientAddress+":"+clientPort);

                SocketThread socketThread = new SocketThread(client);
                socketThread.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static class SocketThread extends Thread {
        private Socket client;
        public SocketThread(Socket client) {
            this.client = client;
        }

        public void run() {
            try {

                OutputStream is = client.getOutputStream();

                InetAddress inetAddress = client.getInetAddress();
                String clientAddress = inetAddress.getHostAddress();
                int clientPort = client.getPort();

                OutputStreamWriter osw = new OutputStreamWriter(is);

//                int max = 4; // flink还没来得及执行 checkpoint,重启后,从11开始算
                int max = 17;
                for (int i = 1; i < max; i++) {
                    String data = "1," + (i + 10)+"\r\n";
                    osw.write(data);
                    osw.flush();
                    System.out.println("send:"+clientAddress+":"+clientPort+", data:"+data);
                    Thread.sleep(1000);
                }
                osw.write("1\r\n"); // 发送错误数据
                osw.flush();
            }catch (Exception e){
                System.out.println(e);
            }
        }
    };
}

第一次测试

启动 java 测试类

设置 max = 4,最后一条 错误数据 在第4秒发出

启动后,socket 连接 java 测试类

结果

由于 checkpoint 每 5 秒执行一次,也就是说,还没有 执行 checkpoint ,程序就出错了,重启时,仍然重新开始算

第二次测试

启动 java 测试类

设置 max = 17,最后一条 错误数据 大约在第17秒发出

启动后,socket 连接 java 测试类

结果

在第 17 秒时,发送错误数据,由于 checkpoint 每 5 秒执行一次,所有有几条记录还没来得及保存

flink 程序崩溃重启后,最近的几条数据 无法恢复

如下图:

观察

D:\flink_cp\6088d78ac90eb147309d1f9ceb7d31a7

目录下的

chk-xxx

文件,每间隔 5s 就重新创建一个,其 编号一直增长


原文出处:/show_1IX48qPlMbV5.html