Flink中的状态

由一个任务维护,并且用来计算某个结果的所有数据,都属于这个任务的状态;可以认为状态就是一个本地变量,可以被任务的业务逻辑访问;Flink会进行状态管理,包括状态一致性,故障处理以及高效存储和访问,以使开发人员可以专注于应用程序的逻辑。

在Flink中,状态始终与特定算子相关联,为了使运行的Flink了解算子的状态,算子需要预先注册其状态。

总的来说,有两种类型的状态:

算子状态(Operator State): 算子状态的作用范围限定为算子任务。

键控状态(Keyed State): 根据输入数据流中定义的键(key)来维护和访问。

算子状态(Operator State)

算子状态的作用范围限定为算子任务,由同一个并行任务所处理的所有数据都可以访问到相同的状态,状态对于同一任务而言使共享的,算子状态不能由相同或不同算子的另一个任务访问。

算子状态数据结构

列表状态(List State): 将状态表示为一组数据的列表。

联合列表状态(Union list state): 也将状态表示为数据的列表,它与常规列表状态的区别在于,在发生故障时,或者从保存点(savepoint)启动应用程序时如何恢复。

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

键控状态(Keyed State)

键控状态时根据输入数据流中定义的键(key)来维护和访问的,Flink为每隔key维护一个状态实例,并将具有相同键的所有数据,都分区到同一个算子任务中,这个任务会维护和处理这个key对应的状态,当文物处理一条数据时,它会自动将状态的访问范围限定为当前数据的key。

键控状态数据结构

值状态(Value state): 将状态表示为单个的值。

列表状态(list state): 将状态表示为一组数据的列表。

映射状态(Map state): 将状态表示为一组key-value对。

聚合状态(Reducing state & aggregating State): 将状态表示为一个用于聚合操作的列表。

键控状态的使用(值状态(Value state)举例:)

声明一个键控状态

//定义一个状态,用来保存上一个数据的温度值
//用懒加载的方式,一开始定义的时候我们还不执行,等到调用的时候去执行
//所有的状态都这么定义 当成一个变量直接用
lazy val lastTemp: ValueState[Double] = getRuntimeContext.getState(new ValueStateDescriptor[Double]("lastTemp", classOf[Double]))

读取状态

//每来一条数据的时候,从状态中取出上一次的温度值

val preTemp = lastTemp.value()

对状态赋值

//更新温度值

 lastTemp.update(value.temperature)

状态后端(State Backends)

每传入一条数据,有状态的算子任务都会读取和更新状态。由于有效的状态访问对于处理数据的低延迟至关重要,因此每隔并行任务都会在本地维护其状态,以确保快速的状态访问。状态的存储,访问以及维护,由一个可插入的组件决定,这个组件就叫做状态呀

后端(state backend),状态后端主要负责两件事:一是本地的状态管理,以及将检查点(checkpoint)状态写入远程存储。

    //开启checkpoint 传入参数代表每隔多久进行checkpoint
env.enableCheckpointing(60000) //状态后端
// env.setStateBackend(new MemoryStateBackend())
// env.setStateBackend(new RocksDBStateBackend(""))

状态编程小应用(三种方式实现)

/**
* 需求:
* 检测两次温度超过一定的范围的话 就报警
* 传感器的温度 跳变太大
*/

方式一:继承 KeyedProcessFunction 抽象类
class TempChangeAlert(thresHold: Double) extends KeyedProcessFunction[String, SensorReading, (String, Double, Double)] {
//定义一个状态变量,保存上次的温度值
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 = { //获取上一次的温度值
val lastTemp = lastTempState.value() //用当前的温度值和上次的温度值计算一个差值,如果大于预设值,就报警‘
val diff = (value.temperature - lastTemp).abs
if(lastTemp==0.0){
println(s"方法一,第一条数据进来,$value")
}else if(diff > thresHold) {
out.collect((value.id, lastTemp, value.temperature))
} //做完一次处理之后将状态进行更新
lastTempState.update(value.temperature)
}
}

 

方式二:继承 RichFlatMapFunction 抽象类
class TempChangeAlertFlatMap(thresHold: Double) extends RichFlatMapFunction[SensorReading,(String,Double,Double)]{

  //定义
private var lastTempState:ValueState[Double] = _ override def open(parameters: Configuration): Unit = {
//初始化的时候声明变量
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(lastTemp==0.0){
println(s"方法二,第一条数据进来,$value")
}else if(diff > thresHold) {
out.collect((value.id, lastTemp, value.temperature))
} //做完一次处理之后将状态进行更新
lastTempState.update(value.temperature)
}
}

方式三:keyBy后调用 flatMapWithState 方法

 //实现方式3
val processedStream4 = dataStream.keyBy(_.id)
.flatMapWithState[(String,Double,Double),Double]{
//没有状态的话,也就是没有数据来过,那么就将当前数据温度值存入状态
case (input:SensorReading,None) => {
println(s"方法三,第一条数据进来,$input") (List.empty,Some(input.temperature))
}
//如果有状态的话,就与上一次的温度值比较差值,如果大于预设值,就报警
case (input:SensorReading,lastTemp:Some[Double]) => {
val diff = (input.temperature - lastTemp.get).abs
if(diff>10.0){
(List((input.id,lastTemp.get,input.temperature)),Some(input.temperature))
}else{
(List.empty,Some(input.temperature))
}
}
}

完整代码:

package com.wyh.statebackendApi

import org.apache.flink.api.common.functions.RichFlatMapFunction
import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.KeyedProcessFunction
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.util.Collector /**
* 需求:
* 检测两次温度超过一定的范围的话 就报警
* 传感器的温度 跳变太大
*/ //温度传感器读数样例类
case class SensorReading(id: String, timestamp: Long, temperature: Double) object Demo1 {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment env.setParallelism(1)
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime) //开启checkpoint 传入参数代表每隔多久进行checkpoint
env.enableCheckpointing(60000) //状态后端
// env.setStateBackend(new MemoryStateBackend())
// env.setStateBackend(new RocksDBStateBackend("")) val stream = env.socketTextStream("localhost", 7777) //Transform操作
val dataStream: DataStream[SensorReading] = stream.map(data => {
val dataArray = data.split(",")
SensorReading(dataArray(0).trim, dataArray(1).trim.toLong, dataArray(2).trim.toDouble)
})
//===到来的数据是升序的,准时发车,用assignAscendingTimestamps
//指定哪个字段是时间戳 需要的是毫秒 * 1000
// .assignAscendingTimestamps(_.timestamp * 1000)
//===处理乱序数据
// .assignTimestampsAndWatermarks(new MyAssignerPeriodic())
//==底层也是周期性生成的一个方法 处理乱序数据 延迟1秒种生成水位 同时分配水位和时间戳 括号里传的是等待延迟的时间
.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[SensorReading](Time.seconds(1)) {
override def extractTimestamp(t: SensorReading): Long = {
t.timestamp * 1000
}
}) // val processedStream = dataStream.keyBy(_.id)
// .process(new TempIncreAlert()) //实现方式1
val processedStream2 = dataStream.keyBy(_.id)
.process(new TempChangeAlert(10.0)) //实现方式2
val processedStream3 = dataStream.keyBy(_.id)
.flatMap(new TempChangeAlertFlatMap(10.0)) //实现方式3
val processedStream4 = dataStream.keyBy(_.id)
.flatMapWithState[(String,Double,Double),Double]{
//没有状态的话,也就是没有数据来过,那么就将当前数据温度值存入状态
case (input:SensorReading,None) => {
println(s"方法三,第一条数据进来,$input") (List.empty,Some(input.temperature))
}
//如果有状态的话,就与上一次的温度值比较差值,如果大于预设值,就报警
case (input:SensorReading,lastTemp:Some[Double]) => {
val diff = (input.temperature - lastTemp.get).abs
if(diff>10.0){
(List((input.id,lastTemp.get,input.temperature)),Some(input.temperature))
}else{
(List.empty,Some(input.temperature))
}
}
} processedStream2.printToErr("processedStream2 data") processedStream3.printToErr("processedStream3 data") processedStream4.printToErr("processedStream4 data") dataStream.print("input data") env.execute("StateBackend Test") } } class TempIncreAlert() extends KeyedProcessFunction[String, SensorReading, String] {
//定义一个状态,用来保存上一个数据的温度值
//用懒加载的方式,一开始定义的时候我们还不执行,等到调用的时候去执行
//所有的状态都这么定义 当成一个变量直接用
lazy val lastTemp: ValueState[Double] = getRuntimeContext.getState(new ValueStateDescriptor[Double]("lastTemp", classOf[Double]))
//定义一个状态用来保存定时器的时间戳
lazy val currentTimer: ValueState[Long] = getRuntimeContext.getState(new ValueStateDescriptor[Long]("currentTimer", classOf[Long])) //判断温度连续上升
//跟上一次数据进行比较 如果比较一直大 10秒种内进行报警
//注册一个定时器 把上一次的数据保存成当前的状态
override def processElement(value: SensorReading, ctx: KeyedProcessFunction[String, SensorReading, String]#Context, out: Collector[String]): Unit = {
//每来一条数据的时候,从状态中取出上一次的温度值
val preTemp = lastTemp.value()
var curTimerTs = currentTimer.value() //更新温度值
lastTemp.update(value.temperature) //加个if判断最开始的温度是否为0来判断是否是第一条数据 温度上升且没有设置过定时器,则注册定时器
if (preTemp == 0.0) {
println("这是第一条数据进来")
} else if ((value.temperature > preTemp) && (curTimerTs == 0L)) { val timerTs = ctx.timerService().currentProcessingTime() + 10000L //传入当前时间加1 是时间戳
ctx.timerService().registerProcessingTimeTimer(timerTs)
currentTimer.update(timerTs)
} else if (value.temperature <= preTemp) {
//如果温度下降 或者是第一条数据 删除定时器
ctx.timerService().deleteProcessingTimeTimer(curTimerTs)
//删除定时器之后将状态清空
currentTimer.clear()
}
} //在回调函数中执行定时器到的逻辑
//当前的时间 ctx上下文 out输出信息
override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[String, SensorReading, String]#OnTimerContext, out: Collector[String]): Unit = {
//直接输出报警信息
out.collect(ctx.getCurrentKey + "温度连续上升")
//考虑真实情况,将状态都清空
currentTimer.clear()
}
} class TempChangeAlert(thresHold: Double) extends KeyedProcessFunction[String, SensorReading, (String, Double, Double)] {
//定义一个状态变量,保存上次的温度值
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 = { //获取上一次的温度值
val lastTemp = lastTempState.value() //用当前的温度值和上次的温度值计算一个差值,如果大于预设值,就报警‘
val diff = (value.temperature - lastTemp).abs
if(lastTemp==0.0){
println(s"方法一,第一条数据进来,$value")
}else if(diff > thresHold) {
out.collect((value.id, lastTemp, value.temperature))
} //做完一次处理之后将状态进行更新
lastTempState.update(value.temperature)
}
} class TempChangeAlertFlatMap(thresHold: Double) extends RichFlatMapFunction[SensorReading,(String,Double,Double)]{ //定义
private var lastTempState:ValueState[Double] = _ override def open(parameters: Configuration): Unit = {
//初始化的时候声明变量
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(lastTemp==0.0){
println(s"方法二,第一条数据进来,$value")
}else if(diff > thresHold) {
out.collect((value.id, lastTemp, value.temperature))
} //做完一次处理之后将状态进行更新
lastTempState.update(value.temperature)
}
}

运行测试:

输入数据:

结果:

Flink学习(十八) 状态管理与状态编程的更多相关文章

  1. Flink状态管理与状态一致性(长文)

    目录 一.前言 二.状态类型 2.1.Keyed State 2.2.Operator State 三.状态横向扩展 四.检查点机制 4.1.开启检查点 (checkpoint) 4.2.保存点机制 ...

  2. Oracle学习(十四):管理用户安全性

    --用户(user) SQL> --创建一个名为 grace password是password 的用户,新用户没有不论什么权限 SQL> create user grace identi ...

  3. redux沉思录:基于flux、状态管理、函数式编程的前端状态管理框架

    基于flux和reduce的通信和状态管理机制; 和数据库管理系统一样,redux是一个状态管理系统(或机制). const store = createStore( reducer, compose ...

  4. Dynamic CRM 2013学习笔记(十八)根据主表状态用JS控制子表自定义按钮

    有时要根据主表的审批状态来控制子表上的按钮要不要显示,比如我们有一个需求审批通过后就不能再上传文件了. 首先打开Visual Ribbon Editor, 如下图,我们可以利用Enable Rules ...

  5. Blazor和Vue对比学习(进阶2.2.3):状态管理之状态共享,Blazor的依赖注入和第三方库Fluxor

    Blazor没有提供状态共享的方案,虽然依赖注入可以实现一个全局对象,这个对象可以拥有状态.计算属性.方法等特征,但并不具备响应式.比如,组件A和组件B,都注入了这个全局对象,并引用了全局对象上的数据 ...

  6. Maven学习(十八)-----Maven依赖管理

    其中一个Maven的核心特征是依赖管理.管理依赖关系变得困难的任务一旦我们处理多模块项目(包含数百个模块/子项目). Maven提供了一个高程度的控制来管理这样的场景. 传递依赖发现 这是很通常情况下 ...

  7. 强化学习(十八) 基于模拟的搜索与蒙特卡罗树搜索(MCTS)

    在强化学习(十七) 基于模型的强化学习与Dyna算法框架中,我们讨论基于模型的强化学习方法的基本思路,以及集合基于模型与不基于模型的强化学习框架Dyna.本文我们讨论另一种非常流行的集合基于模型与不基 ...

  8. spring学习 十八 spring的声明事物

    1.编程式事务: 1.1 由程序员编程事务控制代码.commit与rollback都需要程序员决定在哪里调用,例如jdbc中conn.setAutoCimmit(false),conn.commit( ...

  9. Linux学习十八之、善用判断式

    原文地址:http://vbird.dic.ksu.edu.tw/linux_basic/0340bashshell-scripts_3.php 善用判断式 在第十一章中,我们提到过 $? 这个变量所 ...

  10. MYSQL数据库学习十八 数据库维护和性能提高

    18.1 数据备份 可能造成数据损失的原因有: 存储介质故障:保存数据库文件的磁盘设备损坏,用户没有数据库备份导致数据彻底丢失. 用户的错误操作:如误删了某些重要数据,甚至整个数据库. 服务器的彻底瘫 ...

随机推荐

  1. IdentityServer4 快速上手

    IdentityServer4 是一个基于 .NET Core 的 OpenID Connect 实现框架. 基于框架创建可运行的应用,通常还需要多个步骤,添加引用.配置项目.框架初始化.按照一系列步 ...

  2. 中电金信发布两款大模型产品,打通AI+应用“最后一公里”

    近年来,以大模型为代表的人工智能技术已成为引领新一代产业变革的核心动力.2024年政府工作报告首次提出"人工智能+",要求"大力推进现代化产业体系建设,加快发展新质生产力 ...

  3. 助推乡村振兴,四川农担X中电金信大数据智能风控平台彰显“榜样的力量”

    ​在2024年3月召开的两会上,政府工作报告首次提到了要大力发展科技金融.绿色金融.普惠金融.养老金融.数字金融,为深入推进金融高质量发展,走中国特色金融发展之路指明了方向.其中,四川农担作为一家专注 ...

  4. 如何在Linux上使用pbcopy和pbpaste命令

    由于 Linux 和 Mac OS X 是基于 *Nix 的系统,因此许多命令可以在两个平台上运行.但是,某些命令可能在两个平台上都没有,比如 pbcopy 和 pbpast.这些命令仅在 Mac O ...

  5. k8s calico-node错误日志 listen tcp: lookup localhost on 8.8.4.4:53: no such host

    项目场景:K8s搭建 问题描述:查看pods状态,发现 calico-node异常[root@k8s-master ~]# kubectl get pods --all-namespacesNAMES ...

  6. Qt/C++地图轨迹回放/自定义图标/动态平滑移动/导入轨迹数据/支持各种地图包括天地图

    一.前言说明 这个轨迹回放的功能迭代过很多个版本,最初的版本是轨迹点的坐标每次都是删除折线再重新生成折线,后面发现有内存泄漏,地图js中并不会及时的释放没有用的对象,哪怕是用地图提供的clearove ...

  7. Qt编写视频监控系统73-不同视频流不同类型的判断和解析(http/m3u8/rtsp/rtmp等)

    一.前言 这套视频监控系统大概从2018年起步整体框架,一步步积累到现在,中间经历了无数次的各种视频文件.视频流.视频设备的播放测试,比如光视频文件就有mp4/wmv/rmvb/mkv/avi等格式, ...

  8. clip-retrieval检索本地数据集

    clip-retrieval检索本地数据集 from clip_retrieval.clip_client import ClipClient, Modality from tqdm import t ...

  9. react学习之antd

    antd 为 Web 应用提供了丰富的基础 UI 组件,我们还将持续探索企业级应用的最佳 UI 实践.它最初是基于 React 的组件库,但随着技术的发展,现在也提供了基于 Vue.js 的版本--A ...

  10. Jenkins使用问题汇总

    1. 禁止job出现403问题 解决方法: 在系统管理 –> Configure Global Security中调整设置:取消"启用安全(Enable security)" ...