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. CAS实现单点登录流程

    CAS实现单点登录 环境 客户端: www.app1.com CAS服务器: www.cas-server.com 1.浏览器:发起请求 www.app1.com 2. 客户端:Authenticat ...

  2. Android 创建自定义布局

    我们所有的控件都是继承至View类的,而所有的布局都是继承至ViewGroup的,所以我们也可以继承某个view类来实现我们自己的布局或者控件. 引入布局 我们新建一个title.xml的layout ...

  3. 使用jquery插件uploadify上传文件的方法与疑问

    我是学生一枚,专业也不是计算机,但又要用到很多相关技术,所以在技术基础不牢靠的情况下,硬着头皮在做.最近在做一个小项目需要上传图片,而且是需要用ajax的方式.但是利用jquery的ajax方法总会有 ...

  4. 向Oracle数据库中CLOB插入数据报错问题

    今天在项目中向数据库的CLOB属性插入一段篇文章(1000~2000)字就会报一个字符串过长的错误. 网上说用流来处理,没有这么做.这像是一个Bug,只要把插入的数据,默认扩充到2000以上就ok了. ...

  5. .NET4.0下使用Net2.0类库或程序集

    最近在项目上一直使用.net4.0 framework,使用ffmepeg下的一个dll时,提示只能在2.0下运行,解决方法如下: app.config中添加一个配置节:startup <?xm ...

  6. nginx上传模块nginx_upload_module和nginx_uploadprogress_module模块进度显示,如何传递GET参数等。

    ownload:http://www.grid.net.ru/nginx/download/nginx_upload_module-2.2.0.tar.gzconfigure and make : . ...

  7. linux之SQL语句简明教程---BETWEEN

    IN 这个指令可以让我们依照一或数个不连续 (discrete) 的值的限制之内抓出数据库中的值,而BETWEEN 则是让我们可以运用一个范围 (range) 内抓出数据库中的值.BETWEEN 这个 ...

  8. python mysqlDB

    1,Description MySQLdb is a Python DB API-2.0-compliant interface Supported versions: * MySQL version ...

  9. firefox必备扩展

    1.首先,当然要推荐 Adblock Plus 安装地址:https://adblockplus.org/zh_CN/firefox 作用:屏蔽广告,从此和该死的广告说拜拜吧(不论是百度推广,还是别的 ...

  10. CURL常用命令---样例

    原文地址: http://www.thegeekstuff.com/2012/04/curl-examples/ 下载单个文件,默认将输出打印到标准输出中(STDOUT)中 curl http://w ...