Flink Native Kubernetes (二)基于阿里云OSS的checkpoint和savepoint测试

目录

  • 准备
  • 开始
    • 制作镜像
    • 编写测试应用
  • 发布应用
    • 测试checkpoint
    • 测试Savepoint

准备

前提已经有一定flink基础
上一篇文章 环境搭建Demo运行 已经完成基础的Demo试跑
接下来测试 精确一次 语义
source 为kafka
sink 为print
主要测试算子状态和checkpoint、savepoint的情况

开始

阅读官网,可以知道很多Connector支持 精确一次 语义
而且checkpoint和savepoint对于异常重试和重新发布对于数据不丢不重是很关键的基础配置
结合上state的管理,可以极大限度的保证数据的完成性
我们这里使用阿里云的OSS作为state的存储与管理

需要容器可访问的Kafka
需要容器可访问的OSS
可以通过ping的方式查看连通性

制作镜像

阅读官网可知

为使用 flink-oss-fs-hadoop,在启动 Flink 之前,将对应的 JAR 文件从 opt 目录复制到 Flink 发行版中的 plugin 目录下的一个文件夹中,例如:
mkdir ./plugins/oss-fs-hadoop
cp ./opt/flink-oss-fs-hadoop-1.11.2.jar ./plugins/oss-fs-hadoop/

所以需要重新制作Docker镜像,把对应Jar包放到指定目录下,上篇文章的资源中已经做好相关镜像 flink:oss 版本
使用此镜像进行Session的启动,启动参数添加三个条件

-Dfs.oss.endpoint=<OSS-ENDPOINT> \
-Dfs.oss.accessKeyId=<OSS-ACCESSKEYID> \
-Dfs.oss.accessKeySecret=<OSS-ACCESSKEYSECRET>

启动后查看对应flink-conf.yaml可以看到已经增加的K8s的ConfigMap中

编写测试应用

从官网DEMO中初始化应用
增加KAFKA依赖pom

		<dependency>
			<groupId>org.apache.flinkgroupId>
			<artifactId>flink-connector-kafka_2.11artifactId>
			<version>1.11.2version>
		dependency>

启动类

package TestStreaming

import java.util.Properties
import java.util.concurrent.TimeUnit

import org.apache.flink.api.common.restartstrategy.RestartStrategies
import org.apache.flink.api.common.serialization.SimpleStringSchema
import org.apache.flink.api.common.time.Time
import org.apache.flink.core.fs.Path
import org.apache.flink.runtime.state.filesystem.FsStateBackend
import org.apache.flink.streaming.api.CheckpointingMode
import org.apache.flink.streaming.api.environment.CheckpointConfig.ExternalizedCheckpointCleanup
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer
/**
 * @author xuanqisong
 * @date 2021/2/22 15:43
 * @document
 */
object TestKafka {
  def main(args: Array[String]): Unit = {
    // init kafka
    val kafkaProperties = new Properties()
    kafkaProperties.setProperty("bootstrap.servers", "20.20.0.185:9092")
    kafkaProperties.setProperty("group.id", "flink-test-kafka")
    val kafkaConsumer = new FlinkKafkaConsumer[String]("test", new SimpleStringSchema(), kafkaProperties)

    // init flink env
    val flinkEnv = StreamExecutionEnvironment.getExecutionEnvironment
    flinkEnv.setParallelism(1)


    // check point
    // 5S 一次检测
    flinkEnv.enableCheckpointing(5000)
    // 设置模式为精确一次 (这是默认值)
    flinkEnv.getCheckpointConfig.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE)
    // Checkpoint 必须在一分钟内完成,否则就会被抛弃
    flinkEnv.getCheckpointConfig.setCheckpointTimeout(60000)
    // 是否自动起立checkpoint文件
    flinkEnv.getCheckpointConfig.enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION) // 保留
//    flinkEnv.getCheckpointConfig.enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION) // 自动删除
    flinkEnv.setStateBackend(new FsStateBackend("oss://bucket-flink//flink-k8s-test//checkpoint//test-kafka"))
//    flinkEnv.setStateBackend(new FsStateBackend(new Path("file:///D:/checkpoint/testkafka/")))

    // 重启策略
    flinkEnv.setRestartStrategy(RestartStrategies.fixedDelayRestart(10000, Time.of(1, TimeUnit.SECONDS))) // 5表示最大重试次数为5次,10s为延迟时间
    val dataStream = flinkEnv
      .addSource(kafkaConsumer).name("kafka_source").uid("kafka_source")
      .map(s => (1, s)).name("map_key").uid("map_key")
      .keyBy(t => t._1)
      .map(new CheckWordMap).name("map_and_error").uid("map_and_error")
      .print().name("print").uid("print")

    flinkEnv.execute("test-kafka-job")
  }
}

map函数

package TestStreaming

import org.apache.flink.api.common.functions.RichMapFunction
import org.apache.flink.api.common.state.{MapState, MapStateDescriptor, ValueState, ValueStateDescriptor}
import org.apache.flink.api.common.typeinfo.TypeInformation
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.scala.createTypeInformation
import org.apache.flink.api.common.state.StateTtlConfig
import org.apache.flink.api.common.state.ValueStateDescriptor
import org.apache.flink.api.common.time.Time

/**
 * @author xuanqisong
 * @date 2021/2/23 14:44
 * @document
 */
class CheckWordMap extends RichMapFunction[(Int, String), String] {

  private var wordSum: ValueState[String] = _
  private var errorCheck: MapState[String, Long] = _

  override def map(in: (Int, String)): String = {
    val tempWordSum = wordSum.value
    val currentWordSum = if (tempWordSum != null) {
      tempWordSum
    } else {
      ""
    }
    val newWordSum = currentWordSum + "," + in._2
    wordSum.update(newWordSum)

    if (errorCheck.contains(in._2)){
      throw new Exception("Stop thread")
    }else{
      errorCheck.put(in._2, 0L)
    }
    wordSum.value()
  }
  
  override def open(parameters: Configuration): Unit = {
    super.open(parameters)
    wordSum = getRuntimeContext.getState(
      new ValueStateDescriptor[String]("wordSum", TypeInformation.of(classOf[String]))
    )
    val mapStateDescriptor = new MapStateDescriptor[String, Long]("errorCheck", TypeInformation.of(classOf[String]), TypeInformation.of(classOf[Long]))
    // 主动异常MapState的过期时间,测试异常后checkpoint重启对于数据的影响
    val ttlConfig = StateTtlConfig
      .newBuilder(Time.seconds(20))
      .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
      .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
      .build
    mapStateDescriptor.enableTimeToLive(ttlConfig)
    errorCheck = getRuntimeContext.getMapState(
      mapStateDescriptor
    )
  }
}

这里需要注意的是,一定要使用Rich相关函数进行继承,这样才可以使用getRunTimeContext的上下文,对状态进行注册
进行打包发布

mvn clean package

在target文件夹中复制jar到服务器上进行发布

发布应用

测试checkpoint

./flink run -c TestStreaming.TestKafka -d -e kubernetes-session -Dkubernetes.namespace=<name-space> -Dkubernetes.cluster-id=<cluster-id> <jar-path>

这里发布后在监控页面就能看到对应Session中已经存在启动的Job
Flink Native Kubernetes (二)基于阿里云OSS的checkpoint和savepoint测试_第1张图片
向kafka对应的topic内推送数据

a
b
c
我们通过最后的Sink点击进去到TaskManager中查看对应的输出

Flink Native Kubernetes (二)基于阿里云OSS的checkpoint和savepoint测试_第2张图片

此测试样例中 如果推送的数据在20S内出现重复就会主动触发一个异常
Stop Thread 异常,可以通过任务的异常栈看到具体的输出
由于MapState有20秒自动清理机制,所以在超过20S后重复数据依然会写入到ValueState中

至此我们可以看到checkpoint对于 精确一次 语义的支持,而且在对应的oss中可以看到对应的checkpoint产生的数据

测试Savepoint

Savepoint是用户管理的数据,用作应用的更新迭代操作
官网可知,如果对应用算子结构/并发度进行了相应的更改,需要通过算子UID进行重新分配,所以上述测试代码中已经在每个算子中增加了name/uid的名称
进行savepoint的触发

./flink stop -p oss://bucket-flink//flink-k8s-test//savepoint//test-kafka  -e kubernetes-session -Dkubernetes.namespace=<name-space> -Dkubernetes.cluster-id=<cluster-id>  <JOB-ID>

写入savepoint 的oss地址进行触发
执行后可以发现OSS已经存在了对应的savepoint的备份
进行savepoint恢复

./flink run -d -s <SAVE-POINT-PATH> -e kubernetes-session -Dkubernetes.namespace=<name-space> -Dkubernetes.cluster-id=<cluster-id> -c TestStreaming.TestKafka <jar-path>

启动后,可以看Session中又恢复了对应的Job
我们再继续向kafka推送数据
可以看到JobManager的输出数据是根据上一次的数据累加得到
至此已经完成对checkpoint/savepoint的测试

你可能感兴趣的:(Flink-on-K8s,大数据,ETL)