flink进阶篇-09-状态编程和容错机制_flink 状态编程与checkpoint-程序员宅基地

技术标签: flink  大数据  

流式计算分为无状态有状态两种情况。

1)无状态的计算观察每个独立事件,并根据最后一个事件输出结果。例如,流处理应用程序从传感器接收温度读

数,并在温度超过 90 度时发出警告。

2)有状态的计算则会基于多个事件输出结果。

以下是一些例子。

所有类型的窗口。例如,计算过去一小时的平均温度,就是有状态的计算。

所有用于复杂事件处理的状态机。例如,若在一分钟内收到两个相差 20 度以上的温度读数,则发出警告,这

是有状态的计算。

流与流之间的所有关联操作,以及流与静态表或动态表之间的关联操作,都是有状态的计算。

下图展示了无状态流处理和有状态流处理的主要区别。无状态流处理分别接收每条数据记录(图中的黑条),然

后根据最新输入的数据生成输出数据(白条)。

有状态流处理会维护状态(根据每条输入记录进行更新),并基于最新输入的记录和当前的状态值生成输出记录

(灰条)。

 

上图中输入数据由黑条表示。无状态流处理每次只转换一条输入记录,并且仅根据最新的输入记录输出结果

(白条)。有状态流处理维护所有已处理记录的状态值,并根据每条新输入的记录更新状态,因此输出记录(灰条)反

映的是综合考虑多个事件之后的结果。

尽管无状态的计算很重要,但是流处理对有状态的计算更感兴趣。事实上,正确地实现有状态的计算比实现

无状态的计算难得多。旧的流处理系统并不支持有状态的计算,而新一代的流处理系统则将状态及其正确性

视为重中之重

1、有状态的算子和应用程序

Flink 内置的很多算子,数据源 source,数据存储 sink 都是有状态的,流中的数据都是 buffer records

会保存一定的元素或者元数据。例如: ProcessWindowFunction 会缓存输入流的数据,ProcessFunction 会保

存设置的定时器信息等等。

在 Flink 中,状态始终与特定算子相关联。总的来说,有两种类型的状态:

算子状态(operator state)

键控状态(keyed state)

1.1 算子状态(operator state)

算子状态的作用范围限定为算子任务。这意味着由同一并行任务所处理的所有数据都可以访问到相同的状

状态对于同一任务而言是共享的。

算子状态不能由相同或不同算子的另一个任务访问。

 

Flink 为算子状态提供三种基本数据结构:

列表状态(List state)

将状态表示为一组数据的列表。

联合列表状态(Union list state)

也将状态表示为数据的列表。它与常规列表状态的区别在于,在发生故障时,或者从保存点(savepoint)启

动应用程序时如何恢复。

广播状态(Broadcast state)

如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态。

1.2 键控状态(keyed state)

键控状态是根据输入数据流中定义的键(key)来维护和访问的。Flink 为每个键值维护一个状态实例,并将

具有相同键的所有数据,都分区到同一个算子任务中,这个任务会维护和处理这个 key 对应的状态。当任务处理

一条数据时,它会自动将状态的访问范围限定为当前数据的 key。因此,具有相同 key 的所有数据都会访问相同的

状态。

Keyed State 很类似于一个分布式的 key-value map 数据结构,只能用于 KeyedStream(keyBy 算子处理

之后)。

 

Flink 的 Keyed State 支持以下数据类型:

ValueState<T>保存单个的值,值的类型为 T。

get 操作: ValueState.value()

set 操作: ValueState.update(T value)

ListState<T>保存一个列表,列表里的元素的数据类型为 T。基本操作如下:

ListState.add(T value)

ListState.addAll(List<T> values)

ListState.get()返回 Iterable<T>

ListState.update(List<T> values)

MapState<K, V>保存 Key-Value 对。

MapState.get(UK key)

MapState.put(UK key, UV value)

MapState.contains(UK key)

MapState.remove(UK key)

ReducingState<T>

AggregatingState<I, O>

State.clear()是清空操作。

我们可以利用 Keyed State,实现这样一个需求:

检测传感器的温度值,如果连续的两个温度差值超过 10 度,就输出报警。

DataStream<Tuple3<String, Double, Double>> warningStream = dataStream
 .keyBy("id")
 .flatMap(new TempIncreaseWarning(10.0));

这里需要实现一个自定义的 RichFlatMapFuction,具体实现如下:

public static class TempIncreaseWarning extends RichFlatMapFunction<SensorReading, 
Tuple3<String, Double, Double>>{
    private Double threshold;
    TempIncreaseWarning(Double threshold) {
        this.threshold = threshold;
    }
    private ValueState<Double> lastTempState;
    
    @Override
    public void open(Configuration parameters) throws Exception {
        lastTempState = getRuntimeContext().getState(new 
        ValueStateDescriptor<Double>("last-temp", Double.class, Double.MIN_VALUE));
    }
 
    @Override
    public void flatMap(SensorReading value, Collector<Tuple3<String, Double, Double>> 
out) throws Exception {
        Double lastTemp = lastTempState.value();
        lastTempState.update(value.getTemperature());
        if( lastTemp != Double.MIN_VALUE ) {// 跟最新的温度值计算差值,如果大于阈值,那么输出报警
            Double diff = Math.abs(value.getTemperature() - lastTemp);
            if (diff > threshold)
                out.collect( new Tuple3<>(value.getId(), lastTemp, 
                value.getTemperature()) );
        }
    }
}

➢ 通过 RuntimeContext 注册 StateDescriptor

➢ StateDescriptor 以状态 state 的名字和存储的数据类型为参数。

➢ 在 open()方法中创建 state 变量。注意复习之前的 RichFunction 相关知识。

2、状态一致性

当在分布式系统中引入状态时,自然也引入了一致性问题。一致性实际上是"正确性级别"的另一种说法,也就

是说在成功处理故障并恢复之后得到的结果,与没有发生任何故障时得到的结果相比,前者到底有多正确?举例来

说,假设要对最近一小时登录的用户计数。在系统经历故障之后,计数结果是多少?如果有偏差,是有漏掉的计数

还是重复计数?

在flink中对有状态的流处理做了以下特点说明:

1)有状态的流处理,内部每个[算子]任务都可以有自己的状态。

2)对于流处理器内部来说,所谓的状态一致性,其实就是我们所说的计算结果要保证准确。

3)一条数据不应该丢失,也不应该重复计算。

4) 在遇到故障时可以恢复状态,恢复以后的重新计算,结果应该也是完全正确的。

如图:数据重做后,仍然要保持状态的一致性

 

2.1 一致性级别

在流处理中,一致性可以分为 3 个级别:

at-most-once (最多一次)

当任务故障时,最简单的做法是什么都不干,既不恢复丢失的状态,也不重播丢失的数据。 这其实是没有正

确性保障的委婉说法——故障发生之后,计数结果可能丢失。

at-least-once (至少一次)

在大多数的真实应用场景,我们希望不丢失事件。这种类型的保障称为 at-least-once,意思是所有的事件都

得到了处理,而一些事件还可能被处理多次。 这表示计数结果可能大于正确值,但绝不会小于正确值。也就是

说,计数程序在发生故障后可能多算,但是绝不会少算。

exactly-once (精确一次)

恰好处理一次是最严格的保证,也是最难实现的。恰好处理一次语义不仅仅意味着没有事件丢失,还意味着

针对每一个数据,内部状态仅仅更新一次。

曾经,at-least-once 非常流行。第一代流处理器(如 Storm 和 Samza)刚问世时只保证 at-least-once,原因有二:

保证 exactly-once 的系统实现起来更复杂。这在基础架构层(决定什么代表正确,以及 exactly-once 的范围是什么)和实现层都很有挑战性。

流处理系统的早期用户愿意接受框架的局限性,并在应用层想办法弥补(例如使应用程序具有幂等性,或者用批量计算层再做一遍计算)。

最先保证 exactly-once 的系统(Storm TridentSpark Streaming)在性能和表现力这两个方面付出了很

大的代价。为了保证 exactly-once,这些系统无法单独地对每条记录运用应用逻辑,而是同时处理多条(一批)记

录,保证对每一批的处理要么全部成功,要么全部失败。这就导致在得到结果前,必须等待一批记录处理结束。因

此,用户经常不得不使用两个流处理框架(一个用来保证 exactly-once,另一个用来对每个元素做低延迟处理),

结果使基础设施更加复杂。

曾经,用户不得不在保证 exactly-once 与获得低延迟和效率之间权衡利弊。Flink 避免了这种权衡。 Flink 的

一个重大价值在于,它既保证了 exactly-once,也具有低延迟高吞吐的处理能力。

从根本上说,Flink 通过使自身满足所有需求来避免权衡,它是业界的一次意义重大的技术飞跃。尽管这在外

行看来很神奇,但是一旦了解,就会恍然大悟。

2.2 端到端(end-to-end)状态一致性

目前我们看到的一致性保证都是由流处理器实现的,也就是说都是在 Flink 流处理器内部保证的;而在真实应

用中,流处理应用除了流处理器以外还包含了数据源(例如 Kafka)和输出到持久化系统。

端到端的一致性保证,意味着结果的正确性贯穿了整个流处理应用的始终;每一个组件都保证了它自己的一

致性,整个端到端的一致性级别取决于所有组件中一致性最弱的组件。具体可以划分如下:

内部保证 —— 依赖 checkpoint

source 端 —— 需要外部源可重设数据的读取位置

sink 端 —— 需要保证从故障恢复时,数据不会重复写入外部系统

而对于 sink 端,又有两种具体的实现方式:幂等(Idempotent)写入事务性(Transactional)写入

幂等写入

所谓幂等操作,是说一个操作,可以重复执行很多次,但只导致一次结果更改, 也就是说,后面再重复执行

就不起作用了。

 

事务写入

需要构建事务来写入外部系统,构建的事务对应着 checkpoint,等到 checkpoint 真正完成的时候,才把所

有对应的结果写入 sink 系统中。

对于事务性写入,具体又有两种实现方式:预写日志(WAL)两阶段提交(2PC)。DataStream API 提供

GenericWriteAheadSink 模板类和 TwoPhaseCommitSinkFunction 接口,可以方便地实现这两种方式的

事务性写入。

1) 预写日志(Write-Ahead-Log,WAL)

➢ 把结果数据先当成状态保存,然后在收到 checkpoint 完成的通知时, 一次性写入 sink 系统

➢ 简单易于实现,由于数据提前在状态后端中做了缓存,所以无论什么sink 系统,都能用这种方式一批搞定

➢ DataStream API 提供了一个模板类:GenericWriteAheadSink,来实现这种事务性 sink

2) 两阶段提交(Two-Phase-Commit,2PC)

➢ 对于每个 checkpoint,sink 任务会启动一个事务,并将接下来所有接收的数据添加到事务里

➢ 然后将这些数据写入外部 sink 系统,但不提交它们 —— 这时只是“预提交”

➢ 当它收到 checkpoint 完成的通知时,它才正式提交事务,实现结果的真正写入

2PC这种方式真正实现了exactly-once,它需要一个提供事务支持的外部 sink 系统。Flink 提供了

TwoPhaseCommitSinkFunction 接口。

2.3 2PC 对外部 sink 系统的要求

1)外部 sink 系统必须提供事务支持,或者 sink 任务必须能够模拟外部系统上的事务

2)在 checkpoint 的间隔期间里,必须能够开启一个事务并接受数据写入

3)在收到 checkpoint 完成的通知之前,事务必须是“等待提交”的状态。 在故障恢复的情况下,这可能需要一些时间。如果这个时候sink系统关闭事务(例如超时了),那么未提交的数据就会丢失

4)sink 任务必须能够在进程失败后恢复事务

5)提交事务必须是幂等操作

2.4 Source 和 Sink 的一致性说明

不同 Source 和 Sink 的一致性保证可以用下表说明:

 

3、检查点(checkpoint)

Flink 具体如何保证 exactly-once 呢? 它使用一种 种轻量级快照机制 被称为"检查点"(checkpoint)的特

性,在出现故障时将系统重置回正确状态。下面通过简单的类比来解释检查点的作用。

假设你和两位朋友正在数项链上有多少颗珠子,如下图所示。你捏住珠子,边数边拨,每拨过一颗珠子就给

总数加一。你的朋友也这样数他们手中的珠子。当你分神忘记数到哪里时,怎么办呢? 如果项链上有很多珠子,你

显然不想从头再数一遍,尤其是当三人的速度不一样却又试图合作的时候,更是如此(比如想记录前一分钟三人一

共数了多少颗珠子,回想一下一分钟滚动窗口)。

 

于是,你想了一个更好的办法:在项链上每隔一段就松松地系上一根有色皮筋, 将珠子分隔开; 当珠子被拨动

的时候,皮筋也可以被拨动; 然后,你安排一个助手,让他在你和朋友拨到皮筋时记录总数。用这种方法,当有人

数错时,就不必从头开 始数。相反,你向其他人发出错误警示,然后你们都从上一根皮筋处开始重数,助 手则会

告诉每个人重数时的起始数值,例如在粉色皮筋处的数值是多少。

Flink 检查点的作用就类似于皮筋标记。数珠子这个类比的关键点是: 对于指定的皮筋而言,珠子的相对位置

是确定的; 这让皮筋成为重新计数的参考点。总状态(珠子的总数)在每颗珠子被拨动之后更新一次,助手则会保存

与每根皮筋对应的检查点状态,如当遇到粉色皮筋时一共数了多少珠子,当遇到橙色皮筋时又是多少。 当问题出

现时,这种方法使得重新计数变得简单。

总结:

状态流应用的一致检查点,其实就是:

1)所有任务的状态,在某个时间点的一份拷贝一份快照)。而这个时间点,应该是所有任务都恰好处理完一个相同的输入数据的时候。

有状态流应用的一致检查点,其实就是:所有任务的状态,在某个时间点的一份拷贝(一份快照)。而这个时间点,应该是所有任务都恰好处理完一个相同的输入数据的时候。

2)应用状态的一致检查点,是 Flink 故障恢复机制的核心

 

3.1 Flink 的检查点算法

Flink 检查点的核心作用是确保状态正确,即使遇到程序中断,也要正确。记住这一基本点之后,我们用一个

例子来看检查点是如何运行的。Flink 为用户提供了用来定义状态的工具。

下图表示程序的初始状态: 输入流中的 6 条记录被检查点分割线(checkpoint barrier)隔开,所有的 map 算

子状态均为 0(计数还未开始),按key分组,数据采用累加方式进行最后输出。

所有 key 为 a 的记录将被顶层的 map 算子处理,所有 key 为 b的记录将被中间层的 map 算子处理,所有

key 为 c 的记录则将被底层的 map 算子处理。

 

上图是程序的初始状态。注意,a、b、c 三组的初始计数状态都是 0,即三个圆柱上的值。ckpt 表示检查点

分割线(checkpoint barriers)。每条记录在处理顺序上严格地遵守在检查点之前或之后的规定,例如

["b",2]在检查点之前被处理,["a",2]则在检查点之后被处理。

当该程序处理输入流中的 6 条记录时,涉及的操作遍布 3 个并行实例(节点、CPU内核等)。那么,检查点该如

何保证 exactly-once 呢?

检查点分割线和普通数据记录类似。它们由算子处理,但并不参与计算,而是会触发与检查点相关的行为。

当读取输入流的数据源(在本例中与 keyBy 算子内联) 遇到检查点屏障时,它将其在输入流中的位置保存到持久化

存储中。

如果输入流来自消息传输系统(Kafka),这个位置就是偏移量。Flink 的存储机制是插件化的,持久化存储可以是分布式文件系统,如 HDFS。下图展示了这个过程。

 

当 Flink 数据源(在本例中与 keyBy 算子内联)遇到检查点分界线(barrier)时, 它会将其在输入流中的位置

保存到持久化存储中。这让 Flink 可以根据该位置重启。

检查点像普通数据记录一样在算子之间流动。当 map 算子处理完前 3 条数据并收到检查点分界线时,它们会

将状态以异步的方式写入持久化存储,如下图所示。

 

位于检查点之前的所有记录(["b",2]、["b",3]和["c",1])被 map 算子处理之后的情 况。

此时,持久化存储已经备份了检查点分界线在输入流中的位置(备份操作发生在barrier 被输入算子处理的时

候)。map 算子接着开始处理检查点分界线,并触发将状 态异步备份到稳定存储中这个动作。

当 map 算子的状态备份和检查点分界线的位置备份被确认之后,该检查点操作就可以被标记为完成,如下图

所示。

我们在无须停止或者阻断计算的条件下,在一个逻辑时间点(对应检查点屏障在输入流中的位置)为计算状态拍

了快照。通过确保备份的状态和位置指向同一个逻辑时间点,后文将解释如何基于备份恢复计算,从 而保证

exactly-once。值得注意的是,当没有出现故障时,Flink 检查点的开销极小, 检查点操作的速度由持久化存储的

可用带宽决定。

回顾数珠子的例子: 除了因为数错而需要用到皮筋之外,皮筋会被很快地拨过。

 

检查点操作完成,状态和位置均已备份到稳定存储中。

输入流中的所有数据记录都已处理完成。值得注意的是,备份的状态值与实际的状态值是不同的。备份反

映的是检查点的状态。

如果检查点操作失败,Flink 可以丢弃该检查点并继续正常执行,因为之后的某一个检查点可能会成功。虽然

恢复时间可能更长,但是对于状态的保证依旧很有力。只有在一系列连续的检查点操作失败之后,Flink 才会抛出

错误,因为这通常预示着发生了严重且持久的错误。

现在来看看下图所示的情况: 检查点操作已经完成,但故障紧随其后。

 

在这种情况下,Flink 会重新拓扑(可能会获取新的执行资源),将输入流倒回到上一个检查点,然后恢复状态

并从该处开始继续计算。在本例中,["a",2]、["a",2] 和["c",2]这几条记录将被重播。

下图展示了这一重新处理过程。从上一个检查点开始重新计算,可以保证在剩下的记录被处理之后,得到的

map 算子的状态值与没有发生故障时的状态值一致。

 

Flink 将输入流倒回到上一个检查点屏障的位置,同时恢复 map 算子的状态值。然后,Flink 从此处开始重新

处理。这样做保证了在记录被处理之后,map 算子的状态值与没有发生故障时的一致。

Flink 检查点算法的正式名称是异步分界线快照(asynchronous barrier snapshotting)。该算法大致基于

Chandy-Lamport 分布式快照算法。

检查点是 Flink 最有价值的创新之一,因为它使 Flink 可以保证 exactly-once, 并且不需要牺牲性能。

3.2 恢复状态流程

3.1示例中讲到异常处理步骤,如何进行重置,我们再来聊聊。

1)重启应用

遇到故障之后,第一步就是重启应用

 

2)状态重置

从 checkpoint 中读取状态,将状态重置

从检查点重新启动应用程序后,其内部状态与检查点完成时的状态完全相同

 

3)数据恢复

开始消费并处理检查点到发生故障之间的所有数据

这种检查点的保存和恢复机制可以为应用程序状态提供“精确一次” (exactly-once)的一致性,因为所有

算子都会保存检查点并恢复其所有状 态,这样一来所有的输入流就都会被重置到检查点完成时的位置

 

3.3 检查点分界线(Checkpoint Barrier)

1)Flink 的检查点算法用到了一种称为分界线(barrier)的特殊数据形式,用来把一条流上数据按照不同的检查

点分开

2)分界线之前到来的数据导致的状态更改,都会被包含在当前分界线所属 的检查点中;而基于分界线之后的数据

导致的所有更改,就会被包含在 之后的检查点中

图例:

A)有两个输入流的应用程序,用并行的两个 Source 任务来读取

 

B)JobManager 会向每个 source 任务发送一条带有新检查点 ID 的消息,通过这 种方式来启动检查点

 

C)数据源将它们的状态写入检查点,并发出一个检查点 barrier

D)状态后端在状态存入检查点之后,会返回通知给 source 任务,source 任务就会向 JobManager 确认检查点完成

 

3.4 分界线对齐流程(barrier)

1)开始对齐

• 分界线对齐:barrier 向下游传递,sum 任务会等待所有输入分区的 barrier 到达

• 对于barrier已经到达的分区,继续到达的数据会被缓存

• 而barrier尚未到达的分区,数据会被正常处理

 

2)结束对齐

当收到所有输入分区的 barrier 时,任务就将其状态保存到状态后端的检查点中, 然后将 barrier 继续向下游转发

 

3)下游继续传递

向下游转发检查点 barrier 后,任务继续正常的数据处理

 

4)Sink确认

• Sink 任务向 JobManager 确认状态保存到 checkpoint 完毕

• 当所有任务都确认已成功将状态保存到检查点时,检查点就真正完成了

 

3.5 Flink+Kafka 如何实现端到端的 exactly-once 语义

我们知道,端到端的状态一致性的实现,需要每一个组件都实现,对于 Flink + Kafka 的数据管道系统

(Kafka 进、Kafka 出)而言,各组件怎样保证 exactly-once 语义呢?

内部 —— 利用 checkpoint 机制,把状态存盘,发生故障的时候可以恢复, 保证内部的状态一致性

source —— kafka consumer 作为 source,可以将偏移量保存下来,如果后续任务出现了故障,恢复的时候

可以由连接器重置偏移量,重新消费数据, 保证一致性

sink —— kafka producer 作为 sink,采用两阶段提交 sink,需要实现一个TwoPhaseCommitSinkFunction

内部的 checkpoint 机制我们已经有了了解,那 source 和 sink 具体又是怎样运行的呢?接下来我们逐步做一

个分析。

我们知道 Flink 由 JobManager 协调各个 TaskManager 进行 checkpoint 存储, checkpoint 保存在

StateBackend 中,默认 StateBackend 是内存级的,也可以改为文件级的进行持久化保存。

具体的两阶段提交步骤总结如下:

 

 

 

 

 

第一条数据来了之后,开启一个 kafka 的事务(transaction),正常写入kafka 分区日志但标记为未提交,这

就是“预提交

jobmanager 触发 checkpoint 操作,barrier 从 source 开始向下传递,遇到barrier 的算子将状态存入状态后

端,并通知 jobmanager

sink 连接器收到 barrier,保存当前状态,存入 checkpoint,通知 jobmanager,并开启下一阶段的事务,用

于提交下个检查点的数据

jobmanager 收到所有任务的通知,发出确认信息,表示 checkpoint 完成

sink 任务收到 jobmanager 的确认信息,正式提交这段时间的数据

外部 kafka 关闭事务,提交的数据可以正常消费了。

所以我们也可以看到,如果宕机需要通过 StateBackend 进行恢复,只能恢复所有确认提交的操作。

4、保存点(save points)

1)Flink 还提供了可以自定义的镜像保存功能,就是保存点(savepoints)

2)原则上,创建保存点使用的算法与检查点完全相同,因此保存点可以认为就是具有一些额外元数据的检查点

3)Flink不会自动创建保存点,因此用户(或者外部调度程序)必须明确地触发创建操作

4)保存点是一个强大的功能。除了故障恢复外,保存点可以用于:有计划 的手动备份,更新应用程序,版本迁移,暂停和重启应用,等等

5、状态后端(state backend)

MemoryStateBackend

内存级的状态后端,会将键控状态作为内存中的对象进行管理,将它们存储在 TaskManager 的 JVM 堆上;

而将 checkpoint 存储在 JobManager 的内存中。

FsStateBackend

将 checkpoint 存到远程的持久化文件系统(FileSystem)上。而对于本地状态,跟 MemoryStateBackend

一样,也会存在 TaskManager 的 JVM 堆上。

RocksDBStateBackend

将所有状态序列化后,存入本地的 RocksDB 中存储。

注意:RocksDB 的支持并不直接包含在 flink 中,需要引入依赖:

<dependency>
 <groupId>org.apache.flink</groupId>
 <artifactId>flink-statebackend-rocksdb_2.12</artifactId>
 <version>1.10.1</version>
</dependency>

设置状态后端为 FsStateBackend,并配置检查点和重启策略:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// 1. 状态后端配置
env.setStateBackend(new FsStateBackend(""));
// 2. 检查点配置
env.enableCheckpointing(1000);
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
env.getCheckpointConfig().setCheckpointTimeout(60000);
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
env.getCheckpointConfig().setPreferCheckpointForRecovery(false);
env.getCheckpointConfig().setTolerableCheckpointFailureNumber(0);
// 3. 重启策略配置
// 固定延迟重启(隔一段时间尝试重启一次)
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(
 3, // 尝试重启次数
 100000 // 尝试重启的时间间隔,也可 org.apache.flink.api.common.time.Time
));
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/q287573145/article/details/124117816

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法