Flink不同于其他实时计算的框架之处是它可以提供针对不同的状态进行编程和计算。本篇文章的主要思路如下,大家可以选择性阅读。

  1. Flink的状态分类及不同点。

  2. Flink针对不同的状态进行编程。

  3. 检查点机制和配置。

  4. 状态的存储。

  

  •  Flilnk的状态分类及不同点

    Flink有两种不同的状态分类,一种是Keyed State(键状态),一种是Operator State(算子状态)。

    • Keyed State

      主要是针对KeyedStream中使用,当使用keyBy方法的时候进行计算。 我们都知道在计算的过程中就是将Flink按照<并行operator, key> 进行计算,每个key又归属于单个Operator,所以我们可以简单的理解为<operator, key>。也就是说首先按Operator分配到不同的实例,然后在不同的实例中,相同的Key分配到相同的组中,然后这些状态就可以在相同的组中进行获取和计算。

    • Operator State

      主要针对不同的算子的状态计算。按照不同的算子如Map, FlatMap,Reduce等算子去分配不同的实例群。像Kafka Connector的例子就很好的应用了这个功能,根据不同的topic去读取不同的状态,比如计算获取到topic的paritition分区和 offset偏移量。 每个算子实例会维护着这个topic的partition及offset的Map状态,这个例子就是很好的使用了Opertator的state。如果Operator并行度发生改变了的话,那么状态也会相应的分配好对应的状态。

  • 可管理的及原生状态

  这两种状态又分为 Managed State (可管理状态)和 Raw State (原生状态)

    • Managed State : 可管理状态就是自己去定义和编写状态处理的逻辑,全部由自己和Flink进行控制。
    • Raw State : 原生状态也就是在Operator算子触发 checkPoint 检查点的时候,Flink会在其数据结构中写入一部分字节码Byte,Flink只能看到其中有一些码,但是无法去进行控制。

  所有的流数据功能都可以使用Managed State,这个也是Flink编程所推荐的。因为要使用Raw State的话比较底层也比较复杂,要实现算子方法时才使用。

  • Flink针对不同的状态进行编程

  我们只针对可管理的状态进行操作,不同的管理 Keyed State 和Operator State 状态原始方法定义可参考官网介绍

    • Keyed State

     我们针对Keyed managed state进行编程。来个场景,假如Flink计算某个功能的时间,如果某个功能Key时间超过某个阈值了则进行计数,如果数据超过了设置的次数,那么直接输出到控制台。直接参考如下代码。

  代码大致的思路是:

  继承RichFlatMapFunction, 定义一个ListState<Long>用于记录当前的状态。

  定义阈值和错误次数值,触发后直接输出控制台下。

  open方法实例化ListState。在里边设置了一下状态的TTL,即状态的生命周期。

  flatMap方法按key分配后的value进行判断和记录。

  最后main方法进行数据准备和输出。

package myflink.state;

import org.apache.commons.compress.utils.Lists;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.state.ListState;
import org.apache.flink.api.common.state.ListStateDescriptor;
import org.apache.flink.api.common.state.StateTtlConfig;
import org.apache.flink.api.common.time.Time;
import org.apache.flink.api.common.typeinfo.TypeHint;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector; import java.util.ArrayList;
import java.util.List; public class ThresholdWarning extends RichFlatMapFunction<Tuple2<String, Long>, Tuple2<String, List<Long>>> { //通过ListState来存储非正常数据的状态
private transient ListState<Long> abnormalData;
//需要监控的阈值
private Long threshold;
//触发报警的次数
private Integer numberOfTimes; public ThresholdWarning(Long threshold, Integer numberOfTimes) {
this.threshold = threshold;
this.numberOfTimes = numberOfTimes;
} @Override
public void open(Configuration parameters) throws Exception { ListStateDescriptor listStateDescriptor = new ListStateDescriptor<Long>("abnormal-state",
TypeInformation.of(Long.class)); //状态存活生命周期设置TTL Time To Live
StateTtlConfig ttlConfig = StateTtlConfig
//设置有效期为10秒
.newBuilder(Time.seconds(10L))
//设置有效的更新规则,当创建和写入的时候需要重新更新为10S
.setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
//设置状态的可见性,设置状态如果没有删除,那么就是可见的,另外一个值:ReturnExpiredIfNotCleanedUp ,
// 如果没有清理的话,状态会一直可见的
.setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
.build(); //设置TTL配置
listStateDescriptor.enableTimeToLive(ttlConfig); //通过状态名称(句柄)获取状态实例,如果不存在则会自动建
abnormalData = getRuntimeContext().getListState(new ListStateDescriptor<Long>("abnormal-state",
TypeInformation.of(Long.class)));
} @Override
public void flatMap(Tuple2<String, Long> value, Collector<Tuple2<String, List<Long>>> out) throws Exception {
Long inputValue = value.f1;
//如果输入的值超过阈值,则记录该次不正常的数据信息
if(inputValue >= threshold) {
abnormalData.add(inputValue);
} ArrayList<Long> list = Lists.newArrayList(abnormalData.get().iterator()); //如果不正常的数据超过了指定的数量,则输出报警信息
if(list.size() >= numberOfTimes) {
out.collect(Tuple2.of(value.f0 + " 超过指定阈值数量", list));
//报警信息输出后,清空状态
abnormalData.clear();
}
} public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); //设置并行度为1,用于观察输出
// env.setParallelism(1); DataStreamSource<Tuple2<String, Long>> tuple2DataStreamSource = env.fromElements(
Tuple2.of("a", 50L), Tuple2.of("a", 80L), Tuple2.of("a", 400L),
Tuple2.of("a", 100L), Tuple2.of("a", 200L), Tuple2.of("a", 200L),
Tuple2.of("b", 100L), Tuple2.of("b", 200L), Tuple2.of("b", 200L),
Tuple2.of("b", 500L), Tuple2.of("b", 600L), Tuple2.of("b", 700L)); tuple2DataStreamSource
.keyBy(0)
//超过100的阈值3次后输出报警信息
.flatMap(new ThresholdWarning(100L, 3))
.printToErr(); env.execute("Managed Keyed State");
}
}

  输出的结果如下,大于等于100的出现3次即进行输出。和我们想象的都一样。

1> (b 超过指定阈值数量,[100, 200, 200])
1> (b 超过指定阈值数量,[500, 600, 700])
3> (a 超过指定阈值数量,[400, 100, 200])
    • operator state

  我们还在原来基础的例子上调整一下,不按key,按Operator类型,只要超过时间的次数达到了就要输出。在其中,把Operator的hashCode进行输出一下,用于区分是否为相同的Operator。首先我们将并行度设置为1,然后一会儿再把并行度调整成2。

  代码的大致思路为:

  继承RichFlatMapFunction,实现CheckpointedFunction接口,即在触发检查点的时候进行操作。

  initializeState方法的时候将opertor的状态和检查点状态进行初始化。

  snapshotState方法即存储状态时将当时的镜像进行存储。可以存储到外部设备。

  flatMap的时候进行阈值判断和数据收集。

  main方法进行检查点设置,数据准备,执行和输出。

package myflink.state;

import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.state.ListState;
import org.apache.flink.api.common.state.ListStateDescriptor;
import org.apache.flink.api.common.typeinfo.TypeHint;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.runtime.state.FunctionInitializationContext;
import org.apache.flink.runtime.state.FunctionSnapshotContext;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.checkpoint.CheckpointedFunction;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.CheckpointConfig;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector; import java.util.ArrayList;
import java.util.List; public class ThresholdOperatorWarning extends RichFlatMapFunction<Tuple2<String, Long>, Tuple2<String,
List<Tuple2<String, Long>>>> implements CheckpointedFunction { //非正确数据状态
private List<Tuple2<String, Long>> bufferedData; //检查点状态
private transient ListState<Tuple2<String, Long>> checkPointedState; //需要监控的阈值
private Long threshold;
//次数
private Integer numberOfTimes; ThresholdOperatorWarning(Long threshold, Integer numberOfTimes) {
this.threshold = threshold;
this.numberOfTimes = numberOfTimes;
this.bufferedData = new ArrayList<>();
} @Override
public void flatMap(Tuple2<String, Long> value, Collector<Tuple2<String, List<Tuple2<String, Long>>>> out)
throws Exception {
Long inputValue = value.f1; //超过阈值则进行记录
if(inputValue >= threshold) {
bufferedData.add(value);
} //超过指定次数则进行汇总和汇总输出
if(bufferedData.size() >= numberOfTimes) {
//输出状态实例的hashCode
out.collect(Tuple2.of(checkPointedState.hashCode() + "阈值报警! " , bufferedData ));
//清理缓存
bufferedData.clear();
} } @Override
public void snapshotState(FunctionSnapshotContext context) throws Exception {
//当数据进行快照时,将数据存储到checkPointedState
checkPointedState.clear();
for (Tuple2<String, Long> element : bufferedData) {
checkPointedState.add(element);
}
} @Override
public void initializeState(FunctionInitializationContext context) throws Exception {
//这里获取的是operatorStateStore
checkPointedState = context.getOperatorStateStore().getListState(new ListStateDescriptor<Tuple2<String, Long>>(
"abnormalData", TypeInformation.of(new TypeHint<Tuple2<String, Long>>() {})
)); //如果发生重启,则需要从快照中将状态进行恢复
if(context.isRestored()) {
for (Tuple2<String, Long> element : checkPointedState.get()) {
bufferedData.add(element);
}
}
} public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); //开启检查点
env.enableCheckpointing(1000L);
// 其他可选配置如下: // 设置语义,默认是EXACTLY_ONCE
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
// 设置检查点之间最小停顿时间
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
// 设置执行Checkpoint操作时的超时时间
env.getCheckpointConfig().setCheckpointTimeout(60000);
// 设置最大并发执行的检查点的数量
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
// 将检查点持久化到外部存储
env.getCheckpointConfig().enableExternalizedCheckpoints(
CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION); //设置并行度为1
env.setParallelism(1);
//数据源 DataStreamSource<Tuple2<String, Long>> tuple2DataStreamSource = env.fromElements(
Tuple2.of("a", 50L), Tuple2.of("a", 80L), Tuple2.of("a", 400L),
Tuple2.of("a", 100L), Tuple2.of("a", 200L), Tuple2.of("a", 200L),
Tuple2.of("b", 100L), Tuple2.of("b", 200L), Tuple2.of("b", 200L),
Tuple2.of("b", 500L), Tuple2.of("b", 600L), Tuple2.of("b", 700L)
); tuple2DataStreamSource.flatMap(new ThresholdOperatorWarning(100L, 3))
.printToErr(); env.execute("managed Operator State"); } }

  当前并行度为1,结果如下,数据没有按key统计,而是按照里边的值进行统计,符合我们的要求。因为是同一个Operator,所以hashcode是一样的。

(1629838640阈值报警! ,[(a,400), (a,100), (a,200)])
(1629838640阈值报警! ,[(a,200), (b,100), (b,200)])
(1629838640阈值报警! ,[(b,200), (b,500), (b,600)])

  接下来将并行度设置为2,结果如下。我们看一下main里边的数据,符合大于等于100的数据一共有10个,那么两个不同的operator分配的时候这10数据的时候,一个operator分5个,那么满足超过3个的时候才收集并输出。因为5个里边只有一组3个满足,2个不满足所以不会输出,所以符合我们的预期。

1> (475161679阈值报警! ,[(a,100), (a,200), (b,200)])
2> (1633355453阈值报警! ,[(a,400), (a,200), (b,100)])
  • 检查点的机制和配置
    • 检查点的机制

      上边我们程序里边设置了检查点,检查点是当数据进行处理的时候将数据的状态进行记录,当程序出现问题的时候方便恢复。

     可以像这样的情况:  数据源——>  123456789|12345678| 12341234|——>sink。|即检查点,是一个checkpoint barrier,当算子运行计算的时候会把当前的状态进行记录,比如读取Kafka的数据,假如读取到offset=6868,然后将这个值进行了记录, 当这时有机器出现了问题,程序需要进行恢复并执行,那么需要重新读取这条数据再计算。引用一张图片可以有更清楚的认识。

  

    • 检查点的配置

      默认情况下,检查点是关闭着的,我们需要明确开启。其他的一些配置可参考如下内容:

     //开启检查点
env.enableCheckpointing(1000L);
// 其他可选配置如下: // 设置语义,默认是EXACTLY_ONCE
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
// 设置检查点之间最小停顿时间
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
// 设置执行Checkpoint操作时的超时时间
env.getCheckpointConfig().setCheckpointTimeout(60000);
// 设置最大并发执行的检查点的数量
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
// 将检查点持久化到外部存储
env.getCheckpointConfig().enableExternalizedCheckpoints(
CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
    • 检查点手工保存

       flink支持手工将检查点的状态存储到外部,也可以指定存储到HDFS文件。存储到外边是为了程序出现了问题时进行恢复,比如OOM问题。程序升级和重启时也需要重新从检查点进行恢复。

# 触发指定id的作业的Savepoint,并将结果存储到指定目录下
bin/flink savepoint :jobId [:targetDirectory]
  •  状态的存储

    Keyed State和Operator State会存储在内存中,因为数据是持续不断的输入的,当数据量非常大的时候,内存会出现不足的情况,那么我们也是需要将当前的状态进行保存的。官方称为状态后端。

    • Flink的状态保存支持3种方式

      MemoryStateBackend,这种方式是将数据存储在JVM中,这种方式是用于开发。

      FsStateBackend, 即以文件的形式存储到磁盘中,可以是HDFS或本地文件。当JobManger把任务发送给Taskmanger进行计算,此时数据会在JVM中,当触发了checkpoint后才会将数据存储到文件中。

      RocksDBStateBackend,这种形式是介于前边两种的情况,这个是将状态数据到KV数据库中,当触发状态的时候会将数据再持久化到文件中。这样即提高了速度,空间也变得更大了。

    • 状态存储配置

      默认情况是MemoryStateBackend,即内存中。

      剩下两种的配置如下,这种方式只对当前Job有效。RocksDB配置的话需要额外引用一下包。

// 配置 FsStateBackend
env.setStateBackend(new FsStateBackend("hdfs://namenode:60060/flink/checkpoints"));
// 配置 RocksDBStateBackend
env.setStateBackend(new RocksDBStateBackend("hdfs://namenode:60060/flink/checkpoints"))

      通过修改flink-yaml.conf可以对该集群所有作业生效。

state.backend: filesystem
state.checkpoints.dir: hdfs://namenode:60060/flink/checkpoints

     

MemoryStateBackend

Flink系列之状态及检查点的更多相关文章

  1. Flink架构(五)- 检查点,保存点,与状态恢复

    检查点,保存点,与状态恢复 Flink是一个分布式数据处理系统,这种场景下,它需要处理各种异常,例如进程终止.机器故障.网络中断等.因为tasks在本地维护它们的state,Flink必须确保在出现故 ...

  2. Flink系列(0)——准备篇(流处理基础)

    Apache Flink is a framework and distributed processing engine for stateful computations over unbound ...

  3. flink系列-10、flink保证数据的一致性

    本文摘自书籍<Flink基础教程> 一.一致性的三种级别 当在分布式系统中引入状态时,自然也引入了一致性问题.一致性实际上是“正确性级别”的另一种说法,即在成功处理故障并恢复之后得到的结果 ...

  4. Flink中的状态与容错

    1.概述 Flink支持有状态计算,根据支持得不同状态类型,分别有Keyed State和Operator State.针对状态数据得持久化,Flink提供了Checkpoint机制处理:针对状态数据 ...

  5. 微软BI 之SSIS 系列 - 通过设置 CheckPoints 检查点来增强 SSIS Package 流程的重用性

    开篇介绍 通常一个 ETL Package 是由多个控制流和数据流共同组成,有的时候 ETL 的步骤可能会比较多,整个流程执行下来的时间可能比较长.假设在 ETL Package 中包含5个Task, ...

  6. [js高手之路] html5 canvas系列教程 - 状态详解(save与restore)

    本文内容与路径([js高手之路] html5 canvas系列教程 - 开始路径beginPath与关闭路径closePath详解)是canvas中比较重要的概念.掌握理解他们是做出复杂canvas动 ...

  7. Flink系列之流式

    本文仅是自己看书.学习过程中的个人总结,刚接触流式,视野面比较窄,不喜勿喷,欢迎评论交流. 1.为什么是流式? 为什么是流式而不是流式系统这样的词语?流式系统在我的印象中是相对批处理系统而言的,用来处 ...

  8. Flink State Backends (状态后端)

    State Backends 的作用 有状态的流计算是Flink的一大特点,状态本质上是数据,数据是需要维护的,例如数据库就是维护数据的一种解决方案.State Backends 的作用就是用来维护S ...

  9. Apache Flink系列-④有状态函数

    有状态函数:独立于平台的有状态无服务器堆栈   这是一种在现代基础设施上创建高效.可扩展且一致的应用程序的简单方法,无论规模大小.   有状态函数是一种API,它通过为无服务器架构构建的运行时简化了分 ...

随机推荐

  1. struts-dojo的使用

    1.导入struts2-dojo-plugin-2.1.8.jar 2.在用使用dojo的页面引入 <span style="font-size:14px;">< ...

  2. [LC] 443. String Compression

    Given an array of characters, compress it in-place. The length after compression must always be smal ...

  3. [LC] 15. 3Sum

    Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find ...

  4. Qt 编译出现 error LNK2019: 无法解析的外部符号

    编辑完成后执行"构建->执行qmake",完成(必须要执行qmake).

  5. confessed to doing|conform|confined|entitle|

    to admit that you have done something wrong or something that you feel guilty or bad about 坦白:供认,招认: ...

  6. mysql挖掘与探索------第2章 索引1-2 全文索引FULLTEXT

    A 显示表的所有索引: show INDEX from phphi_article; B删除索引:alter table phphi_article drop INDEX fullwords; C添加 ...

  7. linux下文件夹归置方式

    /bin:二进制可执行命令./dev:设备特殊文件./etc:系统管理和配置文件./etc/rc.d:启动的配 置文件和脚本./home:用户主目录的基点,比如用户user的主目录就是/home/us ...

  8. Serializable中的serialVersionUID是必须的吗

    不写serialVersionUID就没有吗 即使不写, jdk反序列化时也会自动检查这个id, 反编译.class文件你也看不到这个值 rpc反序列化 如果使用jdk的方式, 这个必须配置 如果使用 ...

  9. 吴裕雄--天生自然python学习笔记:Python3 日期和时间

    Python 程序能用很多方式处理日期和时间,转换日期格式是一个常见的功能. Python 提供了一个 time 和 calendar 模块可以用于格式化日期和时间. 时间间隔是以秒为单位的浮点小数. ...

  10. jmeter如何解决乱码问题

    使用jmeter的时候时常遇到中文乱码的情况,下面总结一下几个解决方法,方便以后进行复习. 1.添加后置处理器Beanshell PostProcessor,在输入框内写入“prve.setDateE ...