09状态管理和容错机制——Flink

Flink会将状态以二进制的形式全部存储起来

算子状态(operator state 一个任务一个状态) 

             “所有并行的子任务对应着一个状态,也就是到子任务来的所有数据共享一个状态”

    

Flink 为算子状态提供三种基本数据结构:

列表状态(List state) 将状态表示为一组数据的列表。 (会根据并行度的调整直接把之前的状态重新分组重新分配)

联合列表状态(Union list state)也将状态表示为数据的列表。它与常规列表状态的区别在于,在发生故障时,或者从保 存点(savepoint)启动应用程序时如何恢复。 (会把之前的状态广播到每个算子中)

广播状态(Broadcast state)如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态

 

键控状态(keyed state keyby之后)

     

Flink 的 Keyed State 支持以下数据类型:

值状态: ValueState[T]保存单个的值,值的类型为 T。

get 操作: ValueState.value()
set 操作: ValueState.update(value: T) 

列表状态: ListState[T]保存一个列表,列表里的元素的数据类型为 T。

基本操作如下:

ListState.add(value: T) 
ListState.addAll(values: java.util.List[T])
ListState.get()返回 Iterable[T]
ListState.update(values: java.util.List[T])

映射状态:MapState[K, V]保存 Key-Value 对。

MapState.get(key: K)
MapState.put(key: K, value: V)
MapState.contains(key: K)
MapState.remove(key: K)

聚合状态: ReducingState[T] 、AggregatingState[I, O]

State.clear()是清空操作。

状态后端 

管理整个flink程序的状态的存储(插入,更新)、访问、维护,除了管理本地外,还管理checkpoint,将其写入远程管理

提供了三个状态后端类型

内存级的状态后端,一般不用再生产当中,只是用在开发和测试之中。实质生产环境中用的是后面两种

rockdb相当于一个本地数据库,将数据序列化后,存到本地中

env.getCheckpointConfig.setMaxConcurrentCheckpoints(1)  //间隔多久做一次快照保存

env.setStateBackend( new RocksDBStateBackend("") )

 

状态编程

检测两次温度范围如果太大则报警


object ProcessFunctionTestTwo {
  case class SensorReading(id: String, timestamp: Long, temperature: Double)
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

    val stream = env.socketTextStream("localhost", 7777)

    val dataStream = stream.map(data => {
      val dataArray = data.split(",")
      SensorReading(dataArray(0).trim, dataArray(1).trim.toLong, dataArray(2).trim.toDouble)
    })
      // 处理乱序问题
      .assignTimestampsAndWatermarks( new BoundedOutOfOrdernessTimestampExtractor[SensorReading]( Time.seconds(1) ) {
        override def extractTimestamp(element: SensorReading): Long = element.timestamp * 1000
      } )

    val processedStream2 = dataStream.keyBy(_.id)
      .process( new TempChangeAlert2(10.0) ) // 能用状态编程
      // .flatMap( new TempChangeAlert(10.0) ) // 也能用状态编程

    // 这个也能进行状态编程
    val processedStream3 = dataStream.keyBy(_.id)
      .flatMapWithState[(String, Double, Double), Double]{
      // 如果没有状态的话,也就是没有数据来过,那么就将当前数据温度值存入状态
      case ( input: SensorReading, None ) => ( List.empty, Some(input.temperature) )
      // 如果有状态,就应该与上次的温度值比较差值,如果大于阈值就输出报警
      case ( input: SensorReading, lastTemp: Some[Double] ) =>
        val diff = ( input.temperature - lastTemp.get ).abs
        if( diff > 10.0 ){
           // list是一个三元组,    some里面是直接更新温度
          ( List((input.id, lastTemp.get, input.temperature)), Some(input.temperature) )
        } else
          ( List.empty, Some(input.temperature) )
    }

    
    dataStream.print("input data")
    env.execute("process function test")
  }
}
/*
   传了一个参数threshold
 */
class TempChangeAlert2(threshold: Double) extends KeyedProcessFunction[String, SensorReading, (String, Double, Double)]{
  // 1、定义一个状态变量,保存上次的温度值
  lazy val lastTempState: ValueState[Double] = getRuntimeContext.getState( new ValueStateDescriptor[Double]("lastTemp", classOf[Double]) )

  override def processElement(value: SensorReading, ctx: KeyedProcessFunction[String, SensorReading, (String, Double, Double)]#Context, out: Collector[(String, Double, Double)]): Unit = {
    // 2、获取上次的温度值
    val lastTemp = lastTempState.value()
    // 3、用当前的温度值和上次的求差,如果大于阈值,输出报警信息
    val diff = (value.temperature - lastTemp).abs  // 做一个绝对值
    if(diff > threshold){
      out.collect( (value.id, lastTemp, value.temperature) )
    }
    lastTempState.update(value.temperature) // 更新最新一次的状态
  }
}


class TempChangeAlert(threshold: Double) extends RichFlatMapFunction[SensorReading, (String, Double, Double)]{
  // 先定义
  private var lastTempState: ValueState[Double] = _
  override def open(parameters: Configuration): Unit = {
    // 初始化的时候声明state变量  (open的时候能从上下文拿到东西)
    lastTempState = getRuntimeContext.getState(new ValueStateDescriptor[Double]("lastTemp", classOf[Double]))
  }
  override def flatMap(value: SensorReading, out: Collector[(String, Double, Double)]): Unit = {
    // 获取上次的温度值
    val lastTemp = lastTempState.value()
    // 用当前的温度值和上次的求差,如果大于阈值,输出报警信息
    val diff = (value.temperature - lastTemp).abs // 做一个绝对值
    if(diff > threshold){
      out.collect( (value.id, lastTemp, value.temperature) )
    }
    lastTempState.update(value.temperature) // 更新最新一次的状态
  }
}

 

容错机制

如何用checkpoin去保存状态

怎样从检查点去恢复状态

一致性检查点:

在流式处理中,应该按照同一时间点,不是物理意义的同一时刻,而是让数据流动,流动到让所有数据处理完某一个数据的时候。这个时候把他们的状态拍下了,就能保证已经处理完了,虽然没有存。但是能保证已经处理完了。

 

offset是偏移量,当偏移量5保存下了后,也就是说把5这个数已经保存下来了。而且已经处理完了。    

2+4=6    1+3+5=9  ,当前的状态是5这个数从头到尾都处理完了,要保存的是所有的数据把5以前的数据都处理完了之后,也就是5 6 9都保存在状态后端,存在checkpoint

 

Flink会定期保存checkpoint ,如果异常了,如何恢复。

 

从检查点恢复状态

 

      

    

    

注意: 这是说的是内部保证精准一次性,如果输出到外部则就不能保证了。

      

     

                 

                                      

              先暂停从源里面读取数据,然后进行状态保存,保存完后在通知JobManager       

              

                

                  

    


    // 开启检查点 (在去配一个重启策略)
    env.enableCheckpointing(60000)
    // 不同的状态一致性级别的设置
    env.getCheckpointConfig.setCheckpointingMode(CheckpointingMode.AT_LEAST_ONCE)
    // 设置超时时间 操作这个时间就不保存了
    env.getCheckpointConfig.setCheckpointTimeout(100000)
    // 检查点出现异常的时候,就将Checkpoint关闭掉=false
    env.getCheckpointConfig.setFailOnCheckpointingErrors(false)
    // 间隔多久做一次快照保存 (同时进行几个Checkpoint) 默认是1
    //env.getCheckpointConfig.setMaxConcurrentCheckpoints(1)
    // 两次Checkpoint之间的最小时间间隔
    env.getCheckpointConfig.setMinPauseBetweenCheckpoints(100)
    // 开启一个Checkpoint外部的持久化(如果job failed是会被自动清理掉的)
    env.getCheckpointConfig.enableExternalizedCheckpoints(ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION)

     // 重启策略,
    env.setRestartStrategy(RestartStrategies.failureRateRestart(3, org.apache.flink.api.common.time.Time.seconds(300), org.apache.flink.api.common.time.Time.seconds(10)))

 

端到端的精准一次性

  

      

         

         要么全部成功了,要么全部不做(不成功)

       

 

       

        

     

    

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值