如果项链上有很多珠子,大家显然不想从头再数一遍,尤其是当三人的速度不一样却又试图合作的时候,更是如此(比如想记录前一分钟三人一共数了多少颗珠子,回想一下一分钟滚动窗口。
于是,我们可以想一个比较好的办法: 在项链上每隔一段就松松地系上一根有色皮筋,将珠子分隔开; 当珠子被拨动的时候,皮筋也可以被拨动; 然后,你安排一个助手,让他在你和朋友拨到皮筋时记录总数。用这种方法,当有人数错时,就不必从头开始数。相反,你向其他人发出错误警示,然后你们都从上一根皮筋处开始重数,助手则会告诉每个人重数时的起始数值。
在执行流应用程序期间,Flink会定期保存状态的一致检查点
如果发生故障,Flink将会使用最近的检查点来一致恢复应用程序的状态,并重新启动处理流程遇到故障后
第一步就是重新启动
第二步是从 checkpoint 中读取状态,将状态重置
从检查点重新启动应用程序后,其内部状态与检查点完成时的状态完全相同
第三步:开始消费并处理检查点到发生故障之间的所有数据
这种检查点的保存和恢复机制可以为应用程序提供“精确一次”(exactly-once)的一致性,因为所有的算子都会保存检查点并恢复其所有的状态,这样一来所有的输入流就都会被重置到检查点完成时的位置
一种简单的想法
暂停应用,保存状态到检查点,再重新恢复应用
Flink 的改进实现
基于Chandy-Lamport 算法的分布式快照
将检查点的保存和数据处理分离开,不暂停整个应用
检查点分界线(Checkpoint Barrier)
Flink 的检查点算法用到了一种称为分界线(barrier)的特殊形式,用来吧一条流上数据按照不同的检查点分开
分界线之前来的数据导致的状态更改,都会被包含在当前分界线所属的检查点中;而基于分界线之后的数据导致的所有更改,就会被包含在之后的检查点中
在以上的基础上,当数据源收到Checkpoint barrier N 之后会先将自己的状态保存,以读取Kafka资料为例,数据源的状态就是目前它在Kafka 分区的位置,这个状态也会写入到上面提到的表格中。
下游的Operator 1 会开始运算属于Checkpoint barrier N 的数据,当Checkpoint barrier N 跟着这些数据流动到Operator 1 之后,Operator 1 也将属于Checkpoint barrier N 的所有数据都反映在状态中,当收到Checkpoint barrier N 时也会直接对Checkpoint去做快照。
分布式快照可以用来做状态容错,任何一个节点挂掉的时候可以在之前的Checkpoint 中将其恢复。继续以上Process,当多个Checkpoint 同时进行,Checkpoint barrier N 已经流到Job manager 2,Flink job manager 可以触发其他的Checkpoint,比如Checkpoint N + 1,Checkpoint N + 2 等等也同步进行,利用这种机制,可以在不阻挡运算的状况下持续地产生Checkpoint。
完整的表格就可以做容错。
算子状态的作用范围限定为算子任务。这意味着由同一并行任务所处理的所有数据都可以访问到相同的状态,状态对于同一任务而言是共享的。算子状态不能由相同或不同算子的另一个任务访问。
键控状态是根据输入数据流中定义的键(key)来维护和访问的。Flink 为每个键值维护一个状态实例,并将具有相同键的所有数据,都分区到同一个算子任务中,这个任务会维护和处理这个 key 对应的状态。
Flink 为算子状态提供三种基本数据结构 …. Keyed State 支持四种数据类型 …
MemoryStateBackend / FsStateBackend / RocksDBStateBackend
JVM Heap 状态后端会在每一次运算值需要读取状态时,用Java object read / writes 进行读或写,不会产生较大代价,但当Checkpoint 需要将每一个运算值的本地状态放入Distributed Snapshots 的时候,就需要进行序列化。
在Runtime 的本地状态后端让使用者去读取状态的时候会经过磁盘,相当于将状态维护在磁盘里,与之对应的代价可能就是每次读取状态时,都需要经过序列化和反序列化的过程。当需要进行快照时只将应用序列化即可,序列化后的数据直接传输到中央的共享DFS 中。
Flink实际上是用 Watermarks 来实现Event – Time 的功能。
Watermarks 在Flink 中也属于特殊事件,其精髓在于当某个运算值收到带有时间戳“ T ”的 Watermarks 时就意味着它不会接收到新的数据了。使用Watermarks 的好处在于可以准确预估收到数据的截止时间。
举例,假设预期收到数据时间与输出结果时间的时间差延迟5 分钟,那么Flink 中所有的 Windows Operator 搜索3 点至4 点的数据,但因为存在延迟需要再多等5分钟直至收集完4:05 分的数据,此时方能判定4 点钟的资料收集完成了,然后才会产出3 点至4 点的数据结果。这个时间段的结果对应的就是 Watermarks 的部分。
Watermark 就是触发前一窗口的“关窗时间”,一旦触发关门那么以当前时刻为准在窗口范围内的所有所有数据都会收入窗中。只要没有达到水位那么不管现实中的时间推进了多久都不会触发关窗。
Savepoint 跟Checkpoint 的差别在于Checkpoint是Flink 对于一个有状态应用在运行中利用分布式快照持续周期性的产生Checkpoint,而Savepoint 则是手动产生的Checkpoint,Savepoint 记录着流式应用中所有运算元的状态。
Savepoint产生的原理是在Checkpoint barrier 流动到所有的Pipeline 中手动插入从而产生分布式快照,这些分布式快照点即Savepoint。Savepoint 可以放在任何位置保存,当完成变更时,可以直接从Savepoint 恢复、执行。
Flink vs. Blink
在设计一个低延迟、exactly once、流和批统一的,能够支撑足够大体量的复杂计算的引擎时,Spark Streaming 等的劣势就显现出来。
Spark Streaming的本质还是一个基于microbatch计算的引擎。这种引擎一个天生的缺点就是每个microbatch的调度开销比较大,当我们要求的延迟越低,额外的开销就越大。这就导致了Spark Streaming实际上不是特别适合于做秒级甚至亚秒级的计算。
Kafka Streams 是从一个日志系统做起来的,它的设计目标是足够轻量,足够简洁易用。这一点很难满足我们对大体量的复杂计算的需求。
Storm是一个没有批处理能力的数据流处理器,除此之外Storm只提供了非常底层的API,用户需要自己实现很多复杂的逻辑。
简单地说,Blink就是阿里巴巴开发的基于开源Flink的企业版计算引擎。如前面所说,虽然Flink在理论模型和架构方面有很多创新,但是在工程实现上还有不少问题。
2015年以来,阿里巴巴团队主要专注于解决Blink的runtime稳定性和scalability的问题。
在拥有了稳定的runtime之后,开始专注于增强Blink的易用性 。所以在2016年底到现在,阿里巴巴团队大力开发Blink实时计算SQL,通过SQL作为统一API服务于各种复杂业务。
从规范Streaming SQL的语义和标准,到实现UDX、join、aggregation、window等一系列SQL最重要的算子,几乎一手打造了完整的Streaming SQL,并且将这些工作推回了FLink社区,得到Flink社区的认可。
学习建议
End
Real -Time Is The Future . 下期见啦 ~