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. php单例设计模式

    class car { static $obj = null; private function __construct(){} static function getObj(){ if(is_nul ...

  2. MYSQL分页存储过程及事务处理--转自peace

    MYSQL的分页过程,和事务处理的一个测试过程. /* --名称:MYSQL版查询分页存储过程 by peace 2013-8-14 --输入参数:@fields -- 要查询的字段用逗号隔开 --输 ...

  3. 智能电视TV开发---如何实现程序省电

    对于很多使用智能手机的用户来,很多抱怨手机耗电太快,很多人买手机的时候卖家都是推荐买两块电池,还有如果用户留心的话,在买手机的网页上,卖家会显示播放视频多长时间,听音乐多长时间,待机多长时间,不过看的 ...

  4. JSP作为img的src时chrome的bug

    废话不说,直接上代码. 新建一个jsp文件,名为img.jsp,代码如下: <%@page import="javax.imageio.ImageIO"%> <% ...

  5. Android4大组件

    http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html 快乐阅读: http://www.360doc.com/cont ...

  6. PHP QR Code封装二维码生成教程

    今天搞了一下二维码封装在tp框架上运用. 找了下草料网, api接口要收费, 现在找到了两种方法来实现用PHP来实现创建二维码. 由于二维码生成,会使用到PHP的GD库, 我们要先在PHP.ini文件 ...

  7. Android Studio导入GitHub

    方法一:引用开源项目的compile添加到gradle中http://www.zhihu.com/question/27027667 方法二:下载安装包引入:http://blog.csdn.net/ ...

  8. ——转 token 介绍

    学习Token Token是什么? Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Tok ...

  9. SQL Server 创建索引的 5 种方法

    前期准备: create table Employee (            ID int not null primary key,            Name nvarchar(4),  ...

  10. Nginx安装配置PHP(FastCGI)环境的教程

    这篇是Nginx安装配置PHP(FastCGI)环境的教程.Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用. 一.什么是 FastCGI F ...