Flink中有两种基本状态:Keyed State和Operator State。
键控状态始终与键相关,并且只能在上的函数和运算符中使用KeyedStream。
您可以将“键控状态”视为已分区或分片的操作员状态,每个键仅具有一个状态分区。每个键状态在逻辑上都绑定到
Keyed State被进一步组织成所谓的Key Groups。密钥组是Flink可以重新分配密钥状态的原子单位;与定义的最大并行度完全相同的键组数。在执行期间,键控运算符的每个并行实例都使用一个或多个键组的键。
使用运算符状态(或非键状态),每个运算符状态都绑定到一个并行运算符实例。对于在Flink中使用操作员状态,Kafka连接器是一个很好的激励示例。Kafka使用者的每个并行实例都维护一个主题分区和偏移量的映射作为其操作员状态。
当更改并行性时,操作员状态接口支持在并行操作员实例之间重新分配状态。可以有不同的方案来进行此重新分配。
要使用托管操作员状态,有状态功能可以实现更通用的CheckpointedFunction 接口,也可以实现ListCheckpointed接口。
该CheckpointedFunction接口通过不同的重新分配方案提供对非键控状态的访问。它需要实现两种方法:
void snapshotState(FunctionSnapshotContext context) throws Exception;
void initializeState(FunctionInitializationContext context) throws Exception;
每当必须执行检查点时,都会snapshotState()被调用。initializeState()每次初始化用户定义的函数时,都会调用对应的,这是在函数首次被初始化时,或者是函数实际上是从较早的检查点恢复时。因此,initializeState()不仅是初始化不同类型状态的地方,而且还包括状态恢复逻辑的地方。
当前,支持列表样式的托管操作符状态。该状态应为彼此独立List的可序列化对象,因此有资格在重新缩放后进行重新分配。换句话说,这些对象是可以重新分配非密钥状态的最佳粒度。根据状态访问方法,定义了以下重新分配方案:
package com.baizhi.jsy.operatorstate
import org.apache.flink.api.common.functions.{RichMapFunction, RuntimeContext}
import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
import org.apache.flink.configuration.Configuration
import org.apache.flink.contrib.streaming.state.RocksDBStateBackend
import org.apache.flink.streaming.api.CheckpointingMode
import org.apache.flink.streaming.api.environment.CheckpointConfig.ExternalizedCheckpointCleanup
import org.apache.flink.streaming.api.scala._
object FlinkWordCountKeyedValueStateCheckPoint {
def main(args: Array[String]): Unit = {
//1.创建流计算执⾏环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setStateBackend(new RocksDBStateBackend("hdfs:///flink-rocksdb-checkpoints",true))
//间隔5s执行一次checkpoint 精准一次
env.enableCheckpointing(5000,CheckpointingMode.EXACTLY_ONCE)
//设置检查点超时 4s
env.getCheckpointConfig.setCheckpointTimeout(4000)
//开启本次检查点 与上一次完成的检查点时间间隔不得小于 2s 优先级高于 checkpoint interval
env.getCheckpointConfig.setMinPauseBetweenCheckpoints(2000)
//如果检查点失败,任务宣告退出 setFailOnCheckpointingErrors(true)
env.getCheckpointConfig.setTolerableCheckpointFailureNumber(0)
//设置如果任务取消,系统该如何处理理检查点数据
//RETAIN_ON_CANCELLATION:如果取消任务的时候,没有加--savepoint,系统会保留留检查
//DELETE_ON_CANCELLATION:取消任务,⾃自动是删除检查点(不不建议使⽤用)
env.getCheckpointConfig.enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.
RETAIN_ON_CANCELLATION)
//2.创建DataStream - 细化
val text = env.socketTextStream("Centos",9999)
//3.执⾏DataStream的转换算⼦
val counts = text.flatMap(line=>line.split("\\s+"))
.map((_,1))
.keyBy(0)
.map(new WordCountMapFunction)
.uid("wc-map")
//4.将计算的结果在控制打印
counts.addSink(new UserDefineBufferSinkEvenSplit(3))
.uid("buffer-sink")
//5.执⾏流计算任务
env.execute("Window Stream WordCount")
}
}
class WordCountMapFunction extends RichMapFunction[(String,Int),(String,Int)]{
var vs:ValueState[Int]=_
override def open(parameters: Configuration): Unit = {
//创建对应状态描述符
val vsd = new ValueStateDescriptor[Int]("wordCount",createTypeInformation[Int])
//获取RuntimeContext
val context:RuntimeContext = getRuntimeContext
//获取指定的类型状态
vs = context.getState(vsd)
}
override def map(in: (String, Int)): (String, Int) = {
//获取历史值
val historyState = vs.value()
//更新状态
vs.update(historyState+in._2)
//返回最新值
(in._1,vs.value())
}
}
package com.baizhi.jsy.operatorstate
import org.apache.flink.api.common.state.{ListState, ListStateDescriptor}
import org.apache.flink.runtime.state.{FunctionInitializationContext, FunctionSnapshotContext}
import org.apache.flink.streaming.api.checkpoint.CheckpointedFunction
import org.apache.flink.streaming.api.functions.sink.SinkFunction
import org.apache.flink.streaming.api.scala._
import scala.collection.JavaConverters._
import scala.collection.mutable.ListBuffer
class UserDefineBufferSinkEvenSplit(threshold:Int=0) extends SinkFunction[(String,Int)] with CheckpointedFunction{
private var checkpointedState : ListState[(String,Int)] = _
private val bufferedElements = ListBuffer[(String,Int)]()
//复写写出逻辑
override def invoke(value: (String, Int), context: SinkFunction.Context[_]): Unit = {
bufferedElements+=value
if (bufferedElements.size>=threshold){
for (e<- bufferedElements){
println("元素是-----:"+e)
}
bufferedElements.clear()
}
}
//快照 需要将状态数据存储起来
override def snapshotState(functionSnapshotContext: FunctionSnapshotContext): Unit = {
println("state snapshotState")
checkpointedState.clear()
checkpointedState.update(bufferedElements.asJava)
}
//初始化状态逻辑 状态恢复逻辑
override def initializeState(functionInitializationContext: FunctionInitializationContext): Unit = {
println("state initializeState")
//初始化状态 也有可能是故障恢复
val lsd = new ListStateDescriptor[(String,Int)]("list-state",createTypeInformation[(String,Int)])
//默认均分的方式恢复
checkpointedState = functionInitializationContext.getOperatorStateStore.getListState(lsd)
//checkpointedState = functionInitializationContext.getOperatorStateStore.getUnionListState(lsd)
//判断是否是故障恢复的逻辑
if(functionInitializationContext.isRestored){
println("state Restored")
bufferedElements.appendAll(checkpointedState.get().asScala.toList)
}
}
}
该ListCheckpointed接口是的一个更有限的变体CheckpointedFunction,它仅支持列表样式状态,并在还原时使用偶数拆分的重新分配方案。它还需要实现两种方法:
List snapshotState(long checkpointId, long timestamp) throws Exception;
void restoreState(List state) throws Exception;
在snapshotState()操作员上应将对象列表返回给检查点,并且 restoreState在恢复时必须处理此类列表。如果状态不是重新分区,可以随时返回Collections.singletonList(MY_STATE)的snapshotState()。
与其他运营商相比,有状态信息源需要多加注意。为了使对状态和输出集合的更新成为原子性(失败/恢复时仅需一次精确的语义),要求用户从源的上下文中获取锁。
snapshotState:在做系统检查点的时候,用户只需要将需要存储的数据返回即可。
restoreState:直接提供给用户需要恢复状态。
package com.baizhi.jsy.operatorstate
import org.apache.flink.contrib.streaming.state.RocksDBStateBackend
import org.apache.flink.streaming.api.CheckpointingMode
import org.apache.flink.streaming.api.environment.CheckpointConfig.ExternalizedCheckpointCleanup
import org.apache.flink.streaming.api.scala._
object FlinkCounterSource001 {
def main(args: Array[String]): Unit = {
//1.创建流计算执⾏环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setStateBackend(new RocksDBStateBackend("hdfs:///flink-rocksdb-checkpoints",true))
//间隔5s执行一次checkpoint 精准一次
env.enableCheckpointing(5000,CheckpointingMode.EXACTLY_ONCE)
//设置检查点超时 4s
env.getCheckpointConfig.setCheckpointTimeout(4000)
//开启本次检查点 与上一次完成的检查点时间间隔不得小于 2s 优先级高于 checkpoint interval
env.getCheckpointConfig.setMinPauseBetweenCheckpoints(2000)
//如果检查点失败,任务宣告退出 setFailOnCheckpointingErrors(true)
env.getCheckpointConfig.setTolerableCheckpointFailureNumber(0)
//设置如果任务取消,系统该如何处理理检查点数据
//RETAIN_ON_CANCELLATION:如果取消任务的时候,没有加--savepoint,系统会保留留检查
//DELETE_ON_CANCELLATION:取消任务,⾃自动是删除检查点(不不建议使⽤用)
env.getCheckpointConfig.enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.
RETAIN_ON_CANCELLATION)
//2.创建DataStream - 细化
val text = env.addSource(new UserDefineCounterSource)
.uid("UserDefineCounterSource")
//4.将计算的结果在控制打印
text.print("offset")
//5.执⾏流计算任务
env.execute("Stream WordCount")
}
}
package com.baizhi.jsy.operatorstate
import java.util
import org.apache.flink.streaming.api.checkpoint.ListCheckpointed
import org.apache.flink.streaming.api.functions.source.{RichParallelSourceFunction, SourceFunction}
import java.lang.{Long => JLong}
import java.util.Collections
import scala.collection.JavaConverters._
class UserDefineCounterSource extends RichParallelSourceFunction[Long] with ListCheckpointed[JLong]{
private var isRunning = true
private var offset = 0L
//存储状态值
override def snapshotState(l: Long, l1: Long): util.List[Long] = {
println("snapshotState"+offset)
Collections.singletonList(offset)//返回一个不可拆分的集合
}
override def restoreState(list: util.List[JLong]): Unit = {
println("restoreState的值"+list.asScala)
offset=list.asScala.head//取第一个元素
}
override def run(sourceContext: SourceFunction.SourceContext[Long]): Unit = {
val lock = sourceContext.getCheckpointLock
while (isRunning) {
Thread.sleep(1000)
// output and state update are atomic
lock.synchronized({
sourceContext.collect(offset)
offset += 1
})
}
}
override def cancel(): Unit = isRunning = false
}