Working with State

本文翻译自Streaming Guide/ Fault Tolerance / Working with State

----------------------------------------------------------------------------------------

Flink中所有transformation可能都看上去像是方法(在functional processing术语中),但事实上它们都是有状态的Operator。你可以通过使用Flink的状态接口或是将你的方法的实例域计入检查点来让所有transformatiuon(如map, filter等)都变成有状态的(stateful)。你可以通过实现一个接口来将任何实例的域注册作为managed状态。在该情况下以及使用FLink的原生状态接口的情况下,Flink将会自动地对你的状态进行周期性一致性快照,并且将它的值存储起来以防失败的发生。

最终的效果便是将任何形式的状态都更新到无失败执行与失败执行都一样的地步。

首先,我们介绍如何在失败的情况下使得实例的域仍是一致的,然后我们介绍Flink的状态接口。

默认地,状态检查点会存储在JobManager的内存中。为了长时间保留较大的状态,Flink支持将检查点存储在文件系统中(HDFS,S3或挂载后的POSIX文件系统),改设置可以通过配置文件flink-conf.yaml配置或是通过StreamExecutionEnvironment.setStateBackend(…)方法设置。有关如何配置可用的状态后端及其相关信息,请见文档state backend

一、使用Key/Value状态接口

Key/Value状态接口提供了对许多不同类型的状态的访问入口,而这些状态都局限于当前输入element的关键字,这意味着这种类型的状态只可以用于KeyedStream,即通过stream.KeyBy(…)创建的流。

下面我们介绍三种可用的不同类型的状态,以及如何将它们用于程序中去。可用的原始状态如下:

·        ValueState<T>该状态维护一个可以更新和获取的值(如上所述的,它局限于输入element的关键字,所以对于一个Operation,它对于每个关键字可能都有一个值)。该值可以使用update(T)设置,可以使用T value()来获取。

·        ListState<T>:该状态维护一个element列表。你可以追加element,也可以获取一个当前存储的element的Iterable。使用add(T)添加element,通过Iterable<T> get()获取Iterable。

·        ReducingState<T>:改状态维护所有添加到这个状态的值的一个聚合的结果值。此接口与ListState相同,通过add(T)添加element,它需要指定一个ReduceFunction来对值进行聚合操作。

所有类型的状态都有一个clear()方法,它将清除当前活动的关键字(即当前输入lement的关键字)的状态。

开发者需要记住,这些状态对象只是用来与状态衔接的,而状态不一定要存储在这些对象里,它们还可以存储于硬盘或是其他地方。其次需要记住从状态中获取的值是取决于输入element的关键字的,所以如果element的关键字是不一样的,那么在你的用户方法中一次调用得到的值和另一次调用得到的值可能是不一样的。

为了获取状态句柄(handle),你必须创建一个StateDescriptor来维护状态的名字(在下面将会看到我们可以创建多个状态,所以它们需要一个唯一的名字来让我们引用)、状态中的值的类型以及可能的用户指定的方法(如一个ReduceFunction)。根据你想获取的状态类型,你可以创建ValueStateDescriptorListStateDescriptorReducingStateDescriptor之一。

状态可以通过RuntimeContext获取,所以它只在rich function中可用。详情请见链接,本文接下来会举一个简短的例子。RichFunction中可用的RuntimeContext拥有以下方法来访问状态:

·        ValueState<T> getState(ValueStateDescriptor<T>)

·        ReducingState<T> getReducingState(ReducingStateDescritpor<T>)

·        ListState<T> getListState(ListStateDescriptor<T>)

这是一个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)

该例实现了一个计数窗口,我们以第一个域作为关键字(在该例中拥有一样的关键字"1")。该方法存储计数并在ValueState中运行加和,一旦计数到达2,它将发送平均值并且清除状态以从0重新开始。注意,如果输入tuple的第一个域的值不一样,则该方法将为每个输入关键字维护不同的状态。

1.1 Scala DataStream API中的状态

除了上面描述的接口,Scala API可以用一个KeyedStream上的valueState来简化有状态的map()或是flatmap()。该用户方法在一个Option中得到ValueState的当前值,且必须返回一个更新后的值来对状态进行更新。

val
stream: DataStream[(String,
Int)] = ...

val
counts: DataStream[(String,
Int)] =
stream
  .keyBy(_._1)
  .mapWithState((in: (String,
Int), count:
Option[Int])
=>

  count match {
    case Some(c)
=> ( (
in._1, c), Some(c
+ in._2) )
    case None =>
( (
in._1, 0),
Some(in._2)
)

  })

二、将实例域计入检查点

实例的域可以使用Checkpointed接口来计入检查点。

当我们使用用户定义方法来实现Checkpointed接口是,snapshotState(…)方法和restoreState(…)方法将会执行来快照并存储方法的状态。

此外,用户定义方法还可以实现CheckpointNotifier接口来通过notifyCheckpointComplete(long
checkpointId)
方法来接收检查点完成的通知。注意,若是在检查点完成和通知之间发生失效问题,Flink不保证用户方法可以收到通知。所以该通知应当设计为后来的检查点通知可以包括丢失的通知的形式。

上面ValueState的例子可以如下使用实例域:

public
class
CountWindowAverage extends RichFlatMapFunction<Tuple2<Long,
Long>, Tuple2<Long, Long>>
  implements Checkpointed<Tuple2<Long,
Long>> {

  private
Tuple2<Long, Long> sum = null;

  @Override
  public void flatMap(Tuple2<Long,
Long> input, Collector<Tuple2<Long,
Long>> out) throws Exception {

    //
update the count

    sum.f0 += 1;

    //
add the second field of the input value

    sum.f1 +=
input.f1;

    //
if the count reaches 2, emit the average and clear the state

    if (sum.f0 >= 2)
{

      out.collect(new
Tuple2<>(input.f0,
sum.f1 / sum.f0));
      sum = Tuple2.of(0L,
0L);
    }
  }

  @Override
  public void open(Configuration
config) {
    if (sum == null) {
    // only recreate if null
    // restoreState will be called before
open()

    // so this will already set the sum to the
restored value

    sum = Tuple2.of(0L,
0L);
    }
  }

  //
regularly persists state during normal operation

  @Override
  public Serializable snapshotState(long
checkpointId, long
checkpointTimestamp) {
    return sum;
  }

  //
restores state on recovery from failure

  @Override
  public void restoreState(Tuple2<Long,
Long> state) {
    sum = state;
  }
}

三、有状态数据源方法

有状态数据源方法与其他Operator相比,需要额外的一些处理。为了使状态的更新以及输出的收集是原子的(在失效恢复时的恰好执行一次语义时需要是原子的),用户需要从SourceContext获取一个lock

public
static class
CounterSource extends
RichParallelSourceFunction<Long> implements
Checkpointed<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
Long snapshotState(long
checkpointId, long
checkpointTimestamp) {

return
offset;

}

@Override

public
void restoreState(Long
state) {

offset
= state;

}

}

一些Operator在检查点被Flink完全接收时可能需要信息来与外界交流,该情况请见接口flink.streaming.api.checkpoint.CheckpointNotifier

四、迭代Job中的状态检查点

Flink在当前版本中只对没有迭代的job提供保证,在迭代job上开启检查点机制将会导致一个异常。若要强制在迭代程序上使用检查点机制,用户需要在启用检查点时设置一个特殊标志:env.enableCheckpointing(interval,
force = true)。

请注意,in flight in the loop edge的数据(以及由于它们而改变的状态)将会在失效中丢失。

Flink Program Guide (8) -- Working with State :Fault Tolerance(DataStream API编程指导 -- For Java)的更多相关文章

  1. Flink Program Guide (3) -- Event Time (DataStream API编程指导 -- For Java)

    Event Time 本文翻译自DataStream API Docs v1.2的Event Time ------------------------------------------------ ...

  2. Flink Program Guide (2) -- 综述 (DataStream API编程指导 -- For Java)

    v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...

  3. Flink Program Guide (9) -- StateBackend : Fault Tolerance(Basic API Concepts -- For Java)

    State Backends 本文翻译自文档Streaming Guide / Fault Tolerance / StateBackend ----------------------------- ...

  4. Flink Program Guide (7) -- 容错 Fault Tolerance(DataStream API编程指导 -- For Java)

    false false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-n ...

  5. Flink Program Guide (10) -- Savepoints (DataStream API编程指导 -- For Java)

    Savepoint 本文翻译自文档Streaming Guide / Savepoints ------------------------------------------------------ ...

  6. Flink Program Guide (6) -- 窗口 (DataStream API编程指导 -- For Java)

    窗口(Window) 本文翻译自文档Windows ----------------------------------- Flink使用窗口的概念,根据element的时间戳或者其他指标,将可能无限 ...

  7. Flink Program Guide (5) -- 预定义的Timestamp Extractor / Watermark Emitter (DataStream API编程指导 -- For Java)

    本文翻译自Pre-defined Timestamp Extractors / Watermark Emitter ------------------------------------------ ...

  8. Flink Program Guide (4) -- 时间戳和Watermark生成(DataStream API编程指导 -- For Java)

    时间戳和Watermark生成 本文翻译自Generating Timestamp / Watermarks --------------------------------------------- ...

  9. Flink Program Guide (1) -- 基本API概念(Basic API Concepts -- For Java)

    false false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-n ...

随机推荐

  1. ajax 分页完全代码整理

    /* ajax分页 */ var page_cur = 1; //当前页 var total_num, page_size, page_total_num;//总记录数,每页条数,总页数 functi ...

  2. c语言-函数的定义及传参

    模块化编程 定义: 数据类型 函数名(型参):如果没有数据类型,默认的为int 值传递 地址传递 嵌套调用 递归调用:直接或间接调用本身函数,求可能栈破裂,求阶乘 #include <stdio ...

  3. 利用html5中的localStorage获取网页被访问的次数

    利用html5中的localStorage获取网页被访问的次数 <!DOCTYPE html> <html> <head> <meta charset=&qu ...

  4. 总结:整理 oracle异常错误处理 .

    5.1 异常处理概念 5.1.1 预定义的异常处理 5.1.2 非预定义的异常处理 5.1.3 用户自定义的异常处理 5.1.4  用户定义的异常处理 5.2 异常错误传播 5.2.1 在执行部分引发 ...

  5. PHP根据身份证号码验证、获取星座、生肖和性别函数

    首先介绍一下身份证含义 新的18位身份证号码各位的含义:1-2位省.自治区.直辖市代码:3-4位地级市.盟.自治州代码:5-6位县.县级市.区代码:7-14位出生年月日,比如19670401代表196 ...

  6. Python: xml转json

    1,引言 GooSeeker早在9年前就开始了Semantic Web领域的产品化,MS谋数台和DS打数机是其中两个产品.对web内容做结构化转换和语义处理的主要路线是 XML -> RDF - ...

  7. acdream 1154 Lowbit Sum

    先贴代码,以后再写题解... 首先,直接枚举肯定是会超时的,毕竟n就有10^9那么多... 对于每个数,我们先把它转化为二进制:例:21-->10101: 对于00001~10101,可以分为几 ...

  8. MVC 视频笔记

    1.关闭Jquery的浏览器缓存 $.ajaxSetup({cache:fasle});

  9. QT:轻松获取网页源码

    获取网页源码的小例子,代码很简单,就不多作解释了. 不过一定要注意网页的编码问题,否则会出现乱码的!!! #include <QtCore> #include <QtNetwork& ...

  10. Linux常用C函数---字符测试篇

    函数讲解部分参考http://net.pku.edu.cn/~yhf/linux_c/ isalnum(测试字符是否为英文或数字) 相关函数 isalpha,isdigit,islower,isupp ...