Flink之状态之状态获取
1.什么是状态
对于任何一个操作,都可以被看成是一个函数,比如y=f(x),如果对于同一个x的任何一次输入,得到的y都是相同的,则可以认为这个函数是无状态,否则,这个函数就是有状态的。Flink的一大特点就在于对状态的支持。
2.Keyed State和Operator State
Keyed State
Keyed State正如其名,总是和具体的key相关联,也只能在keyedStream的function和operator上使用。
Keyed State可以被当做是Operator State的一种特例,但是被分区或者分片的,对于某个key在某个分区上有唯一的状态。逻辑上,key state总是对应一个 <parallel-operator-instance, key>二元组,某种程度上,由于某个具体的Key总是属于某个具体的并行实例,这种情况下,也可以被简化认为是 <operator, key>。
Keyed State会被组织成Key Group。Key Group是可以被Flink用来进行重分布的最小单元,所以有多少个并发,就会有多少个Key Group。在执行过程中,每个keyed operator的并发实例会处理来自不同key的不同的Key Group。
Operator State
对Operator State而言,每个operator state都对应一个并行实例。Kafka Connector就是一个很好的例子。每个Kafka consumer的并行实例都会持有一份topic partition 和offset的map,这个map就是它的Operator State。
Operator State可以在并行度发生变化的时候将状态在所有的并行实例中进行重分布,并且提供了多种方式来进行重分布。
3.托管状态和非托管状态
Keyed State和Operator State都有两种存在形式,即托管状态和非托管状态。
托管状态可以使用flink runtime提供数据结构来实现,比如internal hash table和RocksDB。具体有ValueState,ListState等。Flink runtime会对这些状态进行编码并写入到checkpoints。
非托管状态使用用户自己的数据结构来实现。当做checkpoints时,非托管状态会以字节流的形式被写入checkpoints。Flink对托管状态的数据结构一无所知,只认为他们是一堆字节数组。
datastream的所有function都可以使用托管状态,非托管状态只能在实现operator的时候使用。相对于非托管状态,推荐使用托管状态,因为如果使用托管状态,Flink可以自动帮你进行状态重分布,也可以更好的做内存管理。
注意:如果你的托管状态需要特殊的序列化,目前Flink还不支持。
4.使用托管Keyed State
有如下的状态可以使用:
ValueState<T>
:保持一个可以更新和获取的值(每个Key一个value),可以用来update(T)更新,用来T value()获取。
ListState<T>
: 保持一个值的列表,用add(T)
或者 addAll(List<T>)来添加,用Iterable<T> get()来获取。
ReducingState<T>
: 保持一个值,这个值是状态的很多值的聚合结果,接口和ListState类似,但是可以用相应的ReduceFunction来聚合。
AggregatingState<IN, OUT>
:保持很多值的聚合结果的单一值,与ReducingState相比,不同点在于聚合类型可以和元素类型不同,提供AggregateFunction来实现聚合。
FoldingState<T, ACC>
: 与AggregatingState类似,除了使用FoldFunction进行聚合。
MapState<UK, UV>
: 保持一组映射,可以将kv放进这个状态,使用put(UK, UV)
or putAll(Map<UK, UV>)添加,或者使用get(UK)获取。
所有类型的状态都有一个clear()方法,可以清除当前的状态。
注意:FoldingState已经不推荐使用,可以用AggregatingState来代替。
需要注意,如上的状态对象只用来和状态打交道,可能会被存储在磁盘或者其他地方。另外,你拿到的状态的值是与key相关的,所以在这个实例中拿到的值可能和别的实例中拿到的不一样。
要使用一个状态对象,需要先创建一个StateDescriptor,他包含了状态的名字,状态的值的类型,或许还有一个用户定义的函数,比如ReduceFunction。取决于你要使用的state,你可以创建ValueStateDescriptor
或者 ListStateDescriptor
或者 ReducingStateDescriptor
或者 FoldingStateDescriptor
或者 MapStateDescriptor。
状态只能通过RuntimeContext获取,所以只能是在rich functions里面。通过RuntimeContext可以用下述方法获取状态:
ValueState<T> getState(ValueStateDescriptor<T>)
ReducingState<T> getReducingState(ReducingStateDescriptor<T>)
ListState<T> getListState(ListStateDescriptor<T>)
AggregatingState<IN, OUT> getAggregatingState(AggregatingState<IN, OUT>)
FoldingState<T, ACC> getFoldingState(FoldingStateDescriptor<T, ACC>)
MapState<UK, UV> getMapState(MapStateDescriptor<UK, UV>)
如下是一个使用FlatMapFunction的例子:
public class CountWindowAverage extends RichFlatMapFunction<Tuple2<Long, Long>, Tuple2<Long, Long>> { /**
* The ValueState handle. The first field is the count, the second field a running sum.
*/
private transient ValueState<Tuple2<Long, Long>> sum; @Override
public void flatMap(Tuple2<Long, Long> input, Collector<Tuple2<Long, Long>> out) throws Exception { // access the state value
Tuple2<Long, Long> currentSum = sum.value(); // update the count
currentSum.f0 += 1; // add the second field of the input value
currentSum.f1 += input.f1; // update the state
sum.update(currentSum); // if the count reaches 2, emit the average and clear the state
if (currentSum.f0 >= 2) {
out.collect(new Tuple2<>(input.f0, currentSum.f1 / currentSum.f0));
sum.clear();
}
} @Override
public void open(Configuration config) {
ValueStateDescriptor<Tuple2<Long, Long>> descriptor =
new ValueStateDescriptor<>(
"average", // the state name
TypeInformation.of(new TypeHint<Tuple2<Long, Long>>() {}), // type information
Tuple2.of(0L, 0L)); // default value of the state, if nothing was set
sum = getRuntimeContext().getState(descriptor);
}
} // this can be used in a streaming program like this (assuming we have a StreamExecutionEnvironment env)
env.fromElements(Tuple2.of(1L, 3L), Tuple2.of(1L, 5L), Tuple2.of(1L, 7L), Tuple2.of(1L, 4L), Tuple2.of(1L, 2L))
.keyBy(0)
.flatMap(new CountWindowAverage())
.print(); // the printed output will be (1,4) and (1,5)
5.使用托管Operator State
为了使用托管的Operator State,必须有一个有状态的函数,这个函数比较继承CheckpointedFunction或者ListCheckpointed<T extends Serializable>。
CheckpointedFunction
CheckpointedFunction有如下两个方法需要实现。
void snapshotState(FunctionSnapshotContext context) throws Exception; void initializeState(FunctionInitializationContext context) throws Exception;
当checkpoint执行的时候,snapshotState()就会被调用。initializeState()会在第一次运行的时候被调用,或者从更早的checkpoint恢复的时候被调用。
目前,支持List类型的托管状态。状态被期望是一个可序列话的对象的List,彼此独立,这样便于重分布。不同的状态获取方式,会导致不同的重分布策略:
Even-split redistribution:每个operator会返回一组状态,所有的状态就变成了统一的状态。在重分布或者恢复的时候,一组状态会被按照并行度分为子组,每个operator会得到一个子组。
Union redistribution: 每个operator会返回一组状态,所有的状态一起组成了统一的状态。在重分布或者恢复的时候,每个operator都会得到所有的状态。
如下示例是一个有状态的SinkFunction使用CheckpointedFunction来在发送到外部之前缓存数据,使用了Even-split策略。
public class BufferingSink
implements SinkFunction<Tuple2<String, Integer>>,
CheckpointedFunction { private final int threshold; private transient ListState<Tuple2<String, Integer>> checkpointedState; private List<Tuple2<String, Integer>> bufferedElements; public BufferingSink(int threshold) {
this.threshold = threshold;
this.bufferedElements = new ArrayList<>();
} @Override
public void invoke(Tuple2<String, Integer> value) throws Exception {
bufferedElements.add(value);
if (bufferedElements.size() == threshold) {
for (Tuple2<String, Integer> element: bufferedElements) {
// send it to the sink
}
bufferedElements.clear();
}
} @Override
public void snapshotState(FunctionSnapshotContext context) throws Exception {
checkpointedState.clear();
for (Tuple2<String, Integer> element : bufferedElements) {
checkpointedState.add(element);
}
} @Override
public void initializeState(FunctionInitializationContext context) throws Exception {
ListStateDescriptor<Tuple2<String, Integer>> descriptor =
new ListStateDescriptor<>(
"buffered-elements",
TypeInformation.of(new TypeHint<Tuple2<String, Integer>>() {})); checkpointedState = context.getOperatorStateStore().getListState(descriptor); if (context.isRestored()) {
for (Tuple2<String, Integer> element : checkpointedState.get()) {
bufferedElements.add(element);
}
}
}
}
initializeState
有一个形参FunctionInitializationContext,用来初始化non-keyed状态容器。
注意上面代码中是如何初始化的,也是调用了StateDescriptor
来传递状态名字和状态的值的类型,如下:
ListStateDescriptor<Tuple2<String, Integer>> descriptor =
new ListStateDescriptor<>(
"buffered-elements",
TypeInformation.of(new TypeHint<Tuple2<Long, Long>>() {})); checkpointedState = context.getOperatorStateStore().getListState(descriptor);
状态存取方法的命名方式反映了重分布的方式,比如getUnionListState(descriptor),标志着使用list state并使用union 重分布。如果方法命名上没有反映重分布策略,比如getListState(descriptor),意味着最基础的even-split重分布策略会被使用。
初始化之后,使用isRestored()来判断是否是一个错误恢复。如果是,则需要执行错误分配的逻辑。
正如在上面的BufferingSink中所示,在状态初始化的时候恢复出来的ListState被保存在类变量中以便在snapshotState()中使用。然后ListState清空了上次checkpoint的对象,并填充了新的对象,以便做checkpoint。
再说一点,keyed state也可以在initializeState()中初始化,这个可以通过使用FunctionInitializationContext来实现。
ListCheckpointed
是一种受限的CheckpointedFunction,只支持List风格的状态和even-spit的重分布策略
List<T> snapshotState(long checkpointId, long timestamp) throws Exception; void restoreState(List<T> state) throws Exception;
snapshotState()会返回一组对象给checkpoint,
restoreState则需要在恢复的时候处理这一组对象。如果状态是不可分区的,则可以在snapshotState()中始终返回Collections.singletonList(MY_STATE)。
Stateful Source Functions
与其他operator相比,有状态的source需要更多的注意,为了使得状态的更新和结果的输出原子化,用户必须在source的context上加锁。
public static class CounterSource
extends RichParallelSourceFunction<Long>
implements ListCheckpointed<Long> { /** current offset for exactly once semantics */
private Long offset; /** flag for job cancellation */
private volatile boolean isRunning = true; @Override
public void run(SourceContext<Long> ctx) {
final Object lock = ctx.getCheckpointLock(); while (isRunning) {
// output and state update are atomic
synchronized (lock) {
ctx.collect(offset);
offset += 1;
}
}
} @Override
public void cancel() {
isRunning = false;
} @Override
public List<Long> snapshotState(long checkpointId, long checkpointTimestamp) {
return Collections.singletonList(offset);
} @Override
public void restoreState(List<Long> state) {
for (Long s : state)
offset = s;
}
}
或许有些operator想知道什么时候checkpoint全部做完了,可以参考使用org.apache.flink.runtime.state.CheckpointListener。
Flink之状态之状态获取的更多相关文章
- Apache Flink中的广播状态实用指南
感谢英文原文作者:https://data-artisans.com/blog/a-practical-guide-to-broadcast-state-in-apache-flink 不过,原文最近 ...
- ListView在编辑状态下不能获取修改后的值,无法更新数据
ListView在编辑状态下不能获取修改后的值,获取到的总是以前的值解决方法:在page_load事件里写: if(!IsPostBack) { ListViewBind(); } 原因:这涉及到as ...
- State Processor API:如何读取,写入和修改 Flink 应用程序的状态
过去无论您是在生产中使用,还是调研Apache Flink,估计您总是会问这样一个问题:我该如何访问和更新Flink保存点(savepoint)中保存的state?不用再询问了,Apache Flin ...
- Flink 源码解析 —— 如何获取 ExecutionGraph ?
https://t.zsxq.com/UnA2jIi 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭建 Flink 1.6. ...
- Flink 源码解析 —— 如何获取 JobGraph?
JobGraph https://t.zsxq.com/naaMf6y 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭建 F ...
- Flink 源码解析 —— 如何获取 StreamGraph?
StreamGraph https://t.zsxq.com/qRFIm6I 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭 ...
- 与众不同 windows phone (23) - Device(设备)之硬件状态, 系统状态, 网络状态
原文:与众不同 windows phone (23) - Device(设备)之硬件状态, 系统状态, 网络状态 [索引页][源码下载] 与众不同 windows phone (23) - Devic ...
- Activity的保存状态和状态恢复
Activity的保存状态和状态恢复 当系统内存不足时,系统会强制结束一些不可见的Activity以节省内存资源.在某些情况下,当被强制结束的Activity再次显示时会出现一些问题. 例如:一个AP ...
- Java线程池状态和状态切换
摘要 介绍线程池的五种状态RUNNING.SHUTDOWN.STOP.TIDYING和TERMINATED,并简述五种状态之间的切换. 在类ThreadPoolExecutor中定义了一个成员变量 ...
随机推荐
- php第五节(字符串函数和时间、日期函数)
<?php //查找字符串函数 // strpos() 查找字符第一次出现的位置 重点区分大小写 //stripos — 查找字符串首次出现的位置(不区分大小写) //strrpos — 计算指 ...
- ETO的公开赛T1《矿脉开采》题解(另类版)
这道题别看是签到题,写起来一点不简单 出题人的正解是双向搜索 我们把物品分成两半 每一半分别跑搜索 答案存下来,用个双指针合并即可 然后我构造了两组数据卡掉了他,不得不缩小数据范围 但我这里为什么要致 ...
- 基于SpringBoot+SpringSecurity+mybatis+layui实现的一款权限系统
这是一款适合初学者学习权限以及springBoot开发,mybatis综合操作的后台权限管理系统 其中设计到的数据查询有一对一,一对多,多对多,联合分步查询,充分利用mybatis的强大实现各种操作, ...
- Struts2的动态方法,及result跳转方式,全局结果以及默认的action的配置
Action动态方法的调用 首先我们需要在struts.xml中去配置一个常量值如下 那么去哪找呢?找到Struts-core.jar并打开 method属性 <action name=&quo ...
- 记js里codePointAt()方法返回的结果的含义。
经过<字符串的扩展>和<字符编码的那些事>这两篇文章的阅读,大概了解js里codePointAt方法返回结果的含义. var str='
- unity开发c#代码
1.摄像头跟随主角移动,并支持旋转. 开发过程中需要摄像头以一定距离跟随player,同时会进行旋转,属于一种常见的跟随方式. using UnityEngine; using System.Coll ...
- crest value &minimum
public class paixu { public static void main(String[] args) { double temp; double num[]={5.1, 7.12, ...
- (长期更新)OI常用模板
代码很简单的模板就不收录了. DFT 离散傅立叶变换 void dft(pdd *a,int l,bool r){ int i,j=l/2,k; for(i=1;i<l;++i){ if(i&l ...
- Yearning和inception搭建MySQL审核平台
前言 采用开源Yearning和inception开源软件,搭建用于MYSQL审核及线上MYSQL语句更新的审核平台. 功能说明 Yearning: 基于Vue.js与Django的整套mysql-s ...
- docker制作jdk+tomcat镜像
docker部署TOMCAT项目 一.内核升级 [root@test01 ~]# uname -r #内核查看确认 2.6.32-696.16.1.el6.x86_64 [root@test01 ...