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之状态之状态获取的更多相关文章

  1. Apache Flink中的广播状态实用指南

    感谢英文原文作者:https://data-artisans.com/blog/a-practical-guide-to-broadcast-state-in-apache-flink 不过,原文最近 ...

  2. ListView在编辑状态下不能获取修改后的值,无法更新数据

    ListView在编辑状态下不能获取修改后的值,获取到的总是以前的值解决方法:在page_load事件里写: if(!IsPostBack) { ListViewBind(); } 原因:这涉及到as ...

  3. State Processor API:如何读取,写入和修改 Flink 应用程序的状态

    过去无论您是在生产中使用,还是调研Apache Flink,估计您总是会问这样一个问题:我该如何访问和更新Flink保存点(savepoint)中保存的state?不用再询问了,Apache Flin ...

  4. Flink 源码解析 —— 如何获取 ExecutionGraph ?

    https://t.zsxq.com/UnA2jIi 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭建 Flink 1.6. ...

  5. Flink 源码解析 —— 如何获取 JobGraph?

    JobGraph https://t.zsxq.com/naaMf6y 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭建 F ...

  6. Flink 源码解析 —— 如何获取 StreamGraph?

    StreamGraph https://t.zsxq.com/qRFIm6I 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭 ...

  7. 与众不同 windows phone (23) - Device(设备)之硬件状态, 系统状态, 网络状态

    原文:与众不同 windows phone (23) - Device(设备)之硬件状态, 系统状态, 网络状态 [索引页][源码下载] 与众不同 windows phone (23) - Devic ...

  8. Activity的保存状态和状态恢复

    Activity的保存状态和状态恢复 当系统内存不足时,系统会强制结束一些不可见的Activity以节省内存资源.在某些情况下,当被强制结束的Activity再次显示时会出现一些问题. 例如:一个AP ...

  9. Java线程池状态和状态切换

    摘要 介绍线程池的五种状态RUNNING.SHUTDOWN.STOP.TIDYING和TERMINATED,并简述五种状态之间的切换.   在类ThreadPoolExecutor中定义了一个成员变量 ...

随机推荐

  1. web网络攻击解决方案

    原文地址:https://www.xingkongbj.com/blog/http/web-attack.html 产生原因 HTTP 不具备安全功能. 在客户端可以篡改请求. 跨站脚本攻击 XSS ...

  2. npm ERR! code: 'EPERM' (权限问题 errro permit)

    PS C:\Users\user\Desktop\test\my-project> npm run iview --save npm ERR! missing script: iview npm ...

  3. BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机)

    Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 2816  Solved: 1246[Submit][Status][Discuss] Descript ...

  4. python 之函数

    一 函数的定义:对功能和动作的封装和定义.二 函数的格式:def 函数名(形参列表): 函数名就是变量名:规则就是变量的规则 函数体(return) ret = 函数名(实参列表)三 函数的返回值:函 ...

  5. git 错误 RPC

    remote: Enumerating objects: 3772, done. error: RPC failed; curl 18 transfer closed with outstanding ...

  6. 在Closing事件中,将e.Cancle设置成true,则Windows无法关机和重启系统的解决办法

    最近在设计一个WinForm程序的时候遇到一个bug,就是From1窗体的关闭事件中设置了e.Cancle设置成true,导致系统无法关机重启,windows7 和windows xp都是这样. 我这 ...

  7. CVE-2017-11882复现-office命令执行

    0x01 前言 11月14日,微软按照惯例发布了11月的安全更新,随后不久,安全公司EMBEDI在官方博客上公开了其向微软提交的编号为CVE-2017-11882的Office远程代码执行漏洞: ht ...

  8. javascript--自定义弹出登陆窗口(弹出窗)

    web开发中浏览器对象封装了诸如prompt.alert.confirm等弹出框,但是有的弹出框并不能满足开发需要,需要我们自己定义弹出框,诸如用户登陆框.消息提示框等.本文利用弹出用户登陆框示例,对 ...

  9. XPath知识点简单总结(思维导图)

    XPath是一种用于在XML文档中查找信息的语言,其对HTML也有很好的支持,所以在网络爬虫中可用于解析HTML文档.参考链接. 下图是XPath知识点的简单总结成思维导图:

  10. linux的date常用命令

    1.显示现在时间 date 2.显示今天日期 date +"%F" date +"%Y-%m-%d" 3.现在时间转化为时间戳 date +%s 4.指定某日期 ...