本文所有理解、资料、图片基本上都是来源于官网,更多的是对官网描述的一个总结和概括。
下图是网上找到的一个应景图片。
State直译上去是”状态”一次,这个有点抽象难以理解。那什么是状态,究竟如何应用到我们的编码过程当中呢?在流式编程中,基本的处理流程是来一条数据,我就处理一条,这种单条和单条数据之间的处理很难形成一个对数据的分析,以下面的Access日志为例,我们对类似于这样的流式数据求一个实时访问量需求。
IP地址 | 服务商 | 页面地址 |
---|---|---|
10.x.x.x | ISP | address |
如果没有state的时候,我们是怎么处理呢?每来一条数据就增加一个1,这个1可以保存在外部存储里,但是这个访问辆是很多的,如果每个流式任务都这样写,那外部存储肯定是受不住的。
State呢这时候可以派出用场,可以把state看作是Flink在计算过程中提供的一个存储数据的盒子,让你把需要用到的数据暂放在这个盒子里面,你用的时候再去盒子里面取,不需要每次都要针对一个存储的状态开一个新的链接去外部访问。
当然Flink自身也使用了State用来来容错处理,这个暂时不表。
上文提到了state是什么,简单的说就是Flink提供放东西的一个小盒子。那么在FlinkStreaming中,Flink提供了keyed Stata。首先简单介绍一下什么是keyedState,简单的说就是一个stream,使用keyby后变成keyedStream。
那么在Flink当中有4种keyed state:
ValueState
: 可以存储任意类型的值,仅可以存储一个对象实例。
ListState
: 可以存储一个任意类型的队列,可以以List的方式存储多个。当然这里Value里面也可以放list.
ReducingState
: 仅可以存储一个值,需要增加一个对应的reduceFunction
AggregatingState
: 与上面类似,就是输出值可以是其他类型
MapState
: 可以存储一个Map
对state来说,正确使用的前提就是了解state的值是和key绑定的,就是一个key对应的一个值。下面这段代码是我从官网找到的一个使用state进行数据两两求平均值的代码
class CountWindowAverage extends RichFlatMapFunction[(Long, Long), (Long, Long)] {
private var sum: ValueState[(Long, Long)] = _
override def flatMap(input: (Long, Long), out: Collector[(Long, Long)]): Unit = {
// access the state value
val tmpCurrentSum = sum.value
// If it hasn't been used before, it will be null
val currentSum = if (tmpCurrentSum != null) {
tmpCurrentSum
} else {
(0L, 0L)
}
// update the count
val newSum = (currentSum._1 + 1, currentSum._2 + input._2)
// update the state
sum.update(newSum)
// if the count reaches 2, emit the average and clear the state
if (newSum._1 >= 2) {
out.collect((input._1, newSum._2 / newSum._1))
sum.clear()
}
}
override def open(parameters: Configuration): Unit = {
sum = getRuntimeContext.getState(
new ValueStateDescriptor[(Long, Long)]("average", createTypeInformation[(Long, Long)])
)
}
}
object ExampleCountWindowAverage extends App {
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.fromCollection(List(
(1L, 3L),
(1L, 5L),
(1L, 7L),
(1L, 4L),
(1L, 2L)
)).keyBy(_._1)
.flatMap(new CountWindowAverage())
.print()
// the printed output will be (1,4) and (1,5)
env.execute("ExampleManagedState")
}