Flink重启策略与广播变量
1 重新启动和故障转移策略
当任务失败时,Flink 需要重新启动失败的任务和其他受影响的任务,将作业恢复到正常状态。
重新启动策略和故障转移策略
用于控制任务重新启动。
- 重新启动策略决定是否以及何时可以重新启动失败/受影响的任务。
- 故障转移策略决定应该重新启动哪些任务以恢复作业。
1.1 重新启动策略
集群可以使用默认的重启策略来启动集群,在没有为作业指定特定的重启策略时,总是使用默认的重启策略。如果提交的作业时指定有重启策略,该策略将覆盖集群的默认设置。
默认的重启策略是通过 Flink 的配置文件 flink-conf.yaml 设置的,每个重启策略都有自己的一组参数来控制其重启行为,这些参数值也在配置文件中设置。除了定义默认的重启策略外,还可以为每个 Flink 作业定义一个特定的重启策略。这些重新启动策略是通过调用 StreamExecutionEnvironment 上的 setRestartStrategy() 方法以编程方式设置的。Fink提供了四类重新启动策略:
重新启动策略类别 | flink-conf.yaml中的对应值 | 描述 |
---|---|---|
Fixed delay | fixed-delay | 固定延迟重新启动策略 |
Exponential Delay | exponential-delay | 指数延迟重新启动策略,最近新增 |
Failure rate | failure-rate | 失败率重新启动策略 |
No restart | none | 无重新启动策略 |
注意:
- 如果没有启用检查点,则使用
no restart
策略。 - 如果检查点被激活,并且没有配置重启策略,则使用
Integer.MAX_VALUE
的固定延迟策略重启尝试。 - 通过代码设置重新启动策略时,ExecutionEnvironment同样适用。
1.1.1 Fixed Delay
固定延迟重新启动策略尝试给定次数来重新启动作业。如果超过了最大尝试次数,作业最终会失败。在两次连续的重启尝试之间,重启策略等待固定的时间。
flink- confi.yaml中设置
参数 | 默认值 | 类型 | 描述 |
---|---|---|---|
restart-strategy | 无 | 指定使用那一类重新启动策略。注意:该参数配置文件中没有,需要新增。应指定值:fixed-delay | |
restart-strategy.fixed-delay.attempts | 1 | Integer | 指定重试执行的次数。 |
restart-strategy.fixed-delay.delay | 1s | Duration | 指定两次连续重启尝试之间的延迟时间。可以指定20s或者1min |
restart-strategy: fixed-delay
restart-strategy.fixed-delay.attempts: 3
restart-strategy.fixed-delay.delay: 10 s
代码中设置
val env = StreamExecutionEnvironment.getExecutionEnvironment()
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(
3, // number of restart attempts
Time.of(10, TimeUnit.SECONDS) // delay
))
1.1.2 Exponential Delay
指数延迟重启策略试图无限地重启作业,随着延迟的增加,直到最大延迟,其间这项工作从不失败。
在两次连续的重启尝试之间,重启策略会呈指数增长,直到达到最大值。然后,它将延迟保持在最大值。
当作业正确执行时,指数延迟值在一段时间后复位;这个阈值是可配置的。
flink- confi.yaml中设置
参数 | 默认值 | 类型 | 描述 |
---|---|---|---|
restart-strategy | 无 | 指定使用那一类重新启动策略。注意:该参数配置文件中没有,需要新增。应指定值:exponential-delay | |
restart-strategy.exponential-delay.backoff-multiplier | 2.0 | Double | 每次失败后,回退值乘以这个值,直到达到最大回退值 ,如果重新启动策略设置为指数延迟。 |
restart-strategy.exponential-delay.initial-backoff | 1s | Duration | 指定重新启动延迟的初始值。可以指定20s或者1min |
restart-strategy.exponential-delay.jitter-factor | 0.1 | Double | 指定回退过程中增加或减少多大幅度的随机值。避免同时重新启动多个作业时,此选项非常有用。 |
restart-strategy.exponential-delay.max-backoff | 5min | Duration | 指定重新启动之间的最高可能持续时间。 |
restart-strategy.exponential-delay.reset-backoff-threshold | 1h | Duration | 它指定作业必须连续正确运行多长时间才能将指数增长的回退重置为其初始值 。可以指定20s或者1min |
restart-strategy: exponential-delay
restart-strategy.exponential-delay.initial-backoff: 10 s
restart-strategy.exponential-delay.max-backoff: 2 min
restart-strategy.exponential-delay.backoff-multiplier: 2.0
restart-strategy.exponential-delay.reset-backoff-threshold: 10 min
restart-strategy.exponential-delay.jitter-factor: 0.1
代码中设置
val env = StreamExecutionEnvironment.getExecutionEnvironment()
env.setRestartStrategy(RestartStrategies.exponentialDelayRestart(
Time.of(1, TimeUnit.MILLISECONDS), // initial delay between restarts
Time.of(1000, TimeUnit.MILLISECONDS), // maximum delay between restarts
1.1, // exponential multiplier
Time.of(2, TimeUnit.SECONDS), // threshold duration to reset delay to its initial value
0.1 // jitter
))
1.1.3 Failure Rate
失败率重启策略是在任务失败后重新启动任务,但当失败率(每一个时间间隔的失败率
)超过时,任务最终会失败。在两次连续的重启尝试之间,重启策略需要等待一定的时间。
flink- confi.yaml中设置
参数 | 默认值 | 类型 | 描述 |
---|---|---|---|
restart-strategy | 无 | 指定使用那一类重新启动策略。注意:该参数配置文件中没有,需要新增。应指定值:failure-rate | |
restart-strategy.failure-rate.delay | 1s | Duration | 指定连续两次尝试重启的延迟时间。可以用20s和1min指定。 |
restart-strategy.failure-rate.failure-rate-interval | 1min | Duration | 测量失效率的时间间隔。可以用20s和1min指定。 |
restart-strategy.failure-rate.max-failures-per-interval | 1 | Integer | 指定时间间隔内作业失败前的最大重启次数。 |
restart-strategy: failure-rate
restart-strategy.failure-rate.max-failures-per-interval: 3
restart-strategy.failure-rate.failure-rate-interval: 5 min
restart-strategy.failure-rate.delay: 10 s
代码中设置
val env = StreamExecutionEnvironment.getExecutionEnvironment()
env.setRestartStrategy(RestartStrategies.failureRateRestart(
3, // max failures per unit
Time.of(5, TimeUnit.MINUTES), //time interval for measuring failure rate
Time.of(10, TimeUnit.SECONDS) // delay
))
1.1.4 none
任务直接失败,没有重新启动的尝试。
flink- confi.yaml中设置
参数 | 默认值 | 类型 | 描述 |
---|---|---|---|
restart-strategy | 无 | 指定使用那一类重新启动策略。注意:该参数配置文件中没有,需要新增。应指定值:none |
restart-strategy: none
代码中设置
val env = StreamExecutionEnvironment.getExecutionEnvironment()
env.setRestartStrategy(RestartStrategies.noRestart())
1.2 故障转移策略
使用集群(flink-conf.yaml)定义的重启策略。这对于支持检查点的流程序很有帮助。缺省情况下,如果没有定义其他重启策略,则选择固定延迟重启策略。
Flink 支持不同的故障转移策略,可以通过配置 flink-conf.yaml 中的 *jobmanager.execution.failover-strategy 参数控制:
故障转移策略类别 | jobmanager.execution.failover-strategy参数对应值 | 备注 |
---|---|---|
Restart all | full —重启job的所有任务 | |
Restart pipelined region | region —重启job的局部的任务 | 该方式为默认值 |
jobmanager.execution.failover-strategy: region
1.2.1 Restart All
该策略重新启动作业中的所有任务以从任务失败中恢复。
1.2.2 Restart pipelined
该策略把任务分成互不相连的区域。当检测到任务失败时,此策略计算必须重新启动以从失败中恢复的最小区域集。对于某些作业,与 Restart All策略相比,这会导致重新启动的任务更少
。
区域是一组通过流水线数据交换进行通信的任务。也就是说,批量数据交换表示一个区域的边界:
- DataStream作业或流Table/SQL作业中的所有数据交换都是流水线的。
- 默认情况下,批处理Table/SQL作业中的所有数据交换都是成批处理的。
- 数据集作业中的数据交换类型由ExecutionMode决定,可以通过ExecutionConfig设置该模式。
重新启动的区域规则如下:
- 包含失败任务的区域将重新启动。
- 如果结果分区在被重新启动的区域时不可用,产生结果分区的区域也将重新启动。
- 如果要重新启动某个区域,则该区域的所有消费者区域也将重新启动。这是为了保证数据一致性,因为不确定性处理或分区可能导致不同的分区。
1.3 重新启动策略案例
将四种类别的重新启动策略进行编码测试,注:该测试不好看效果,需要指定异常观察。
package com.qianfeng.day04
import org.apache.flink.api.common.restartstrategy.RestartStrategies
import org.apache.flink.api.common.time.Time
import org.apache.flink.streaming.api.CheckpointingMode
import org.apache.flink.streaming.api.environment.CheckpointConfig.ExternalizedCheckpointCleanup
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.api.scala._
import org.apache.flink.runtime.state.hashmap.HashMapStateBackend
import org.apache.flink.runtime.state.storage.FileSystemCheckpointStorage
import org.apache.flink.streaming.api.environment.CheckpointConfig
import java.util.concurrent.TimeUnit
/**
* Flink任务失败重启
*/
object Demo01_DataStream_RestartStragy {
def main(args: Array[String]): Unit = {
//1、获取流式执行环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
System.setProperty("HADOOP_USER_NAME","root")
//设置checkpoint
val conf = env.getCheckpointConfig //获取checkpoint的配置
//checkpoint的其它配置
//checkpoint的语义
conf.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE)
//checkpoint配置
conf.isCheckpointingEnabled
//设置取消保留状态
//conf.enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION) //老版本
conf.setExternalizedCheckpointCleanup(ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION)
conf.setCheckpointInterval(1000) //检测间隔
//checkpoint的超时设置
conf.setCheckpointTimeout(60000)
//最大并行checkpoint
conf.setMaxConcurrentCheckpoints(6)
//设置状态后端存储
env.setStateBackend(new HashMapStateBackend)
conf.setCheckpointStorage(new FileSystemCheckpointStorage("hdfs://hadoop01:9000/flinkcheckpoint"))
//失败重启策略
//1、固定延迟重启
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(3,Time.seconds(10)))
/*//2、指数延迟重启
env.setRestartStrategy(RestartStrategies.exponentialDelayRestart(
Time.of(1, TimeUnit.MILLISECONDS), // initial delay between restarts
Time.of(1000, TimeUnit.MILLISECONDS), // maximum delay between restarts
1.1, // exponential multiplier
Time.of(2, TimeUnit.SECONDS), // threshold duration to reset delay to its initial value
0.1 // jitter
))
//3、失败率
env.setRestartStrategy(RestartStrategies.failureRateRestart(1,Time.seconds(10),Time.seconds(10)))
//4、失败不重启
env.setRestartStrategy(RestartStrategies.noRestart())*/
//数据实时处理
val dstream: DataStream[String] = env.socketTextStream("hadoop01", 6666)
//3、对source进行transformer
val res: DataStream[(String, Int)] = dstream.flatMap(_.split(" "))
.map((_, 1))
.keyBy(0)
.sum(1)
//4、对DataStream进行sink
res.print("restart Strategy wc->")
//5、触发执行
env.execute("restart Strategy")
}
}
运行测试:
# 将nc关闭掉,然后运行上诉代码。你会发现该应用不会立即失败,会等待一段时间才会真正失败。
注意:
1、默认使用Fixed Delay Restart Strategy,如果有设置,默认将会被覆盖。
2、Fallback Restart Strategy默认使用的Region。
3、上述的配置值,一般使用案例中的配置即可。具体还是需要根据作业的需求适当上调或者下调。
2 广播状态
Flink 1.5 引入Broadcast State特性。
广播状态(Broadcast State)是 Operator State 的一种特殊类型。
如果我们需要将动态配置 、规则等低吞吐事件流广播到下游所有 Task 时,就可以使用 BroadcastState。
下游的 Task 接收这些动态配置、规则并保存为 BroadcastState,所有Task 中的状态保持一致,作用于另一个数据流的计算中。
如果不使用 Broadcast State,则在每个节点中的每个 Task 中都需要拷贝一份数据集,比较浪费内存(也就是一个节点中可能会存在多份数据)。
2.1 广播状态场景
-
多次使用一份小数据集:在计算过程中需要多次并行处理某个小数据集的情况可以使用广播状态。
-
动态更新计算规则: 如事件流需要根据最新的规则进行计算,则可将**规则(数据量较少的)**作为广播状态广播到下游Task中。
-
实时增加额外字段: 如事件流需要实时增加用户的基础信息,则可将用户的基础信息作为广播状态广播到下游Task中。
2.2 广播状态步骤
1、定义广播状态
构建广播状态描述器(MapStateDescriptor),设置好字段、类型、状态描述器标识。
2、数据流广播并创建广播状态
将获取的数据流进行广播。使用ds.broadcast(广播状态描述器)方法即可进行广播。
3、设置广播变量
在某个需要用到该广播状态的算子后调用ds.connect(广播状态流).process(广播处理函数),广播状态处理函数中便可以进行相关状态和其他操作处理。
2.3 广播状态案例
有一个小静态数据集,我们需要广播到每一个Task,然后再用主流中的某个字段关联上广播状态中的数据,然后再做相关处理后输出。
package com.qianfeng.day04
import org.apache.flink.api.common.state.MapStateDescriptor
import org.apache.flink.api.common.typeinfo.BasicTypeInfo
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.api.scala._
import org.apache.flink.streaming.api.functions.co.BroadcastProcessFunction
import org.apache.flink.util.Collector
/**
* 广播状态broadcast
* 输入:
* 1 zs 1 beijing
* 2 ls 2 shanghai
* 3 ww 6 chengdu
*/
object Demo02_DataStream_Broadcast {
def main(args: Array[String]): Unit = {
//获取执行环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//获取数据源
val ds1: DataStream[(Int, Char)] = env.fromElements((1, '男'), (2, '女'))
val ds2: DataStream[(Int, String, Int, String)] = env.socketTextStream("hadoop01", 6666).map(perLine => {
val arr = perLine.split(" ")
val id = arr(0).trim.toInt
val name = arr(1).trim
val genderFlg = arr(2).trim.toInt
val address = arr(3).trim
(id, name, genderFlg, address)
})
//定义mapState的描述器
val desc = new MapStateDescriptor("genderInfo",
BasicTypeInfo.INT_TYPE_INFO,
BasicTypeInfo.CHAR_TYPE_INFO)
//将ds1进行广播
val bcStream = ds1.broadcast(desc)
//广播状态操作
ds2.connect(bcStream)
//非广播流是keyed,那么函数是一个KeyedBroadcastProcessFunction。 非广播流如果是non-keyed,则函数为BroadcastProcessFunction。
.process(new BroadcastProcessFunction[(Int, String, Int, String), (Int, Char), (Int, String, Char, String)]() {
//处理非广播端元素
override def processElement(value: (Int, String, Int, String),
ctx: BroadcastProcessFunction[(Int, String, Int, String), (Int, Char), (Int, String, Char, String)]#ReadOnlyContext,
out: Collector[(Int, String, Char, String)]): Unit = {
val genderFlg = value._3
var gender = ctx.getBroadcastState(desc).get(genderFlg)
//如果genderFlag取出来是Null,,需要单独处理一下
if(gender == null){
gender = '妖'
}
//输出
out.collect((value._1, value._2, gender, value._4))
}
//处理广播端元素进行处理
override def processBroadcastElement(value: (Int, Char),
ctx: BroadcastProcessFunction[(Int, String, Int, String), (Int, Char), (Int, String, Char, String)]#Context,
out: Collector[(Int, String, Char, String)]): Unit = {
//取出广播变量,然后再重新更新值
ctx.getBroadcastState(desc).put(value._1, value._2)
}
}).print()
//触发
env.execute("broadcast")
}
}
运行代码测试效果如下:
1> (1,zs,男,beijing)
2> (2,ls,女,shanghai)
3> (3,ww,妖,chengdu)
注意:
-
非广播流是keyed,那么函数是一个KeyedBroadcastProcessFunction。 非广播流如果是non-keyed,则函数为BroadcastProcessFunction。
-
processBroadcastElement()中实现的逻辑必须在所有并行实例中具有相同逻辑行为。
-
对于非广播流,广播状态是只读,而对于广播流,状态是可写可读。
-
广播出去的变量存在于每个节点的内存中,所以这个数据集不能太大,否则会出现 OOM
-
广播变量在初始化广播出去以后不支持修改,这样才能保证每个节点的数据都是一致的