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. (转)Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds

    仰天长啸 Server Tomcat v6.0 Server at localhost was unable to start within 45 seconds... 当启动tomcat时候出现 S ...

  2. jeasyui制作计划-ajax学习

    Ajax:可以无刷新状态更新页面,并且实现异步提交,提升了用户的体验. 1.load()函数的使用,可以三个参数:url(必须的参数,请求html文件的url地址,参数类型string).date(可 ...

  3. PLSQL实例(游标)

    在PLSQL块中执行SQL语句 A.   数据定义DDL: create,drop,truncate,不能直接执行,truncate执行时只做数据删除,不写日起,不维护索引 在PLSQL块中执行字符串 ...

  4. vi常用操作命令

    进入vi的命令 vi filename :打开或新建文件,并将光标置于第一行首 vi +n filename :打开文件,并将光标置于第n行首 vi + filename :打开文件,并将光标置于最后 ...

  5. 分析BGARefreshLayout-master

    一.知识点,创建BaseActivity 建立方法的逻辑顺序 并将一些常用的方法填充到其中 ①.将initView().setListener().onClick().processLogic()方法 ...

  6. bzoj 3224: Tyvj 1728 普通平衡树 替罪羊树

    题目链接 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的 ...

  7. Print! Print! Print!

    print语句可以实现打印--只是对程序员友好的标准输出流的接口而已. 从技术角度来讲,这是把一个或多个对象转换为其文本表达形式,然后发送给标准输出或另一个类似文件的流. 更详细地说,在Python中 ...

  8. UVA 12563 Jin Ge Jin Qu hao

    dp-背包 开始用普通dp写了一发发现没法确定最大时间... 后来看到大牛机智的写法,嗯...dp表示当前状态能否成立:然后从条件最好的状态开始遍历,直到这个状态成立然后退出遍历. 具体的看代码吧.. ...

  9. autofac使用笔记

    在之前的项目中用来解耦的使用的轻型IOC框架是unity,它的使用也是很方便的提供在之前的文章的也提到过它的使用方式,但是使用久了之后发现了它的不足之处就是需要配置xml文件来对应的接口和实现的关系. ...

  10. nginx的请求接收流程(一)

    今年我们组计划写一本nginx模块开发以及原理解析方面的书,整本书是以open book的形式在网上会定时的更新,网址为http://tengine.taobao.org/book/index.htm ...