为什么状态很重要
Flink定义的状态
状态是计算的过程中需要保存的一些中间态,这些状态是需要我们手工编写代码去进行缓存以便后续使用的。
首先要理解状态的作用域:
- 任务:任务是Flink提交的一次作业,也就是说,所有的状态都是在一个任务里面的,任务结束的时候,所有的状态都会被清空
- 子任务:flink可以进行并行度的设置,所以在每个环节可能被拆为多个子任务,每个子任务的状态相互隔离,这也被称为
算子状态
。事实上,这样的隔离是我们正所需要的,因为每个子任务处理不同的数据,所以里面计算出来的状态值内容本身也可能不能。除非在一些特殊场景下,需要每个子任务的数据相同(比如读取一些配置,这是通过子任务的广播实现的) - 链:flink是面向流的,数据流根据业务的诉求会被分成很多分流,用keyBy函数中定义Key进行划分,每个分流可以被认为是一个状态空间,这也被称为
链控状态
。flink内部为每个key都维护了一份实例
这也就是flink中的两种状态:算子状态
和链控状态
。一般使用链控状态,因为处理的数据都是以key作为单位维度去处理的。
相关操作理解
理解获取运行上下文:getRuntimeContext
getRuntimeContext: 这个函数是获取运行时的上下文。因为flink是面向流的,所以数据要在里面
因为flink是面向流的,要给流动的数据进行缓存,这里flink有一个机制叫做获取上下文getRunTime
他的入参是一个状态描述符,这个类本身是继承于Serializable接口去存储状态,需要声明类型,如下
getRuntimeContext().getState(new valueStateDescriptor);
需求场景
场景1:跳变发出预警
若变化大于10,进行告警(常见风控、监控需求)
思路:保存上一段数据为一个状态
,用当前的数据 - 上次保存的数据。如果大于阀值(10), 进行告警
val alertStream = dataStream
.keyBt(_.id) // 当前器件的id
.flatMap( new TempChangeAlert(10))
alertSream.print()
env,execute("start test")
// 实现
class TempChangeAlert(threshold: Double) extends RichFlatMapFunction[SensorReading, (String, Double, Double)]{
// 定义状态保存上一次温度
lazy val lastTempState: ValueState[Double] = getRuntimeContext.getState(new ValueStateDescripor[Double](""))
override def flatMap(value: SensorReading, out: Collector[String])
val lastTemp = lasteTempState.value()
val diff = (value.temperature - lastTemp).abs
if ( diff > threshold)
out.collect(( value.id, lastTemp, value.temperature))
}
场景2:统计总接入的用户数
思路:在open阶段,打开一个状态描述器获取流中的信息,然后再map函数中生命一个hashset,将流中拿出的用户id添加到hashset里面
定义自己自己的richfunction,重写open和map函数
// 重写open函数,开辟区域存储状态
public void open() {
stateDescriptor = new ValueStateDescripor<>(
)
myStateValue = getRuntimeContext().getState(stateDescriptor);
}
// 重写map函数,定义set结构体进行统计
public void map(Tuple2<String, Interger> tuple2){
String id = tuple.f0;
hashSet idSet = myStateValue.value();
hashSet.add(id0;)
return hbaseMap;
}
算子探索
RichFunction
RichFunction
是所有flink的函数的方法,可以获取运行环境的上下文,并拥有一些生命周期方法,所以可以实现更复杂的功能。在状态编程中是肯定会涉及到的。
可以看到RichFunction
的底层,是继承于Funcion
类,添加了以下几个方法
open:是RichFunction的初始化,
close:做一些清理
getRuntimeContext:例如函数执行的并行度,任务的名字,以及state状态
setRuntimeContext:设置函数的运行状态
getIterationRuntimeContext:未涉及
其他的AbstractRichFunction
、RichMapFunction
、RichSinkFunction
都是基于以上进行扩展 。
使用
如果要操作有关生命周期相关的: 用RichXXXFunction, 或者直接用RichFunction自己添加接口; 如果要不操作与生命周期相关的: 直接使用map、reduce、flatmap等等方法
附
scala语法
var myVar : String = "Foo" // 声明一个变量
var z:Arrary[String] = new Array[String](3) // 声明一个数组