Flink 有状态流式处理
传统批次处理方法
【1】持续收取数据(kafka等),以window 时间作为划分,划分一个一个的批次档案(按照时间或者大小等);
【2】周期性执行批次运算(Spark/Stom等);
传统批次处理方法存在的问题:
理想方法
累积状态:表示过去历史接收过的所有事件。可以是计数或者机器模型等等。
流式处理
流处理系统或者流处理引擎都是数据驱动的,而不是定期或者人为的去触发。数据也没有物理边界。
分散式流式处理
有状态分散式流式处理
定义一个变量X,输出结果依据这个X,这个X就是一个状态。有状态分散的流失处理引擎,当状态可能会累计非常大。当key比较多的时候就会超出单台节点的负荷量。这个x就应该有状态后台使用memory去维护它。【数据倾斜】
状态容错(State Fault Tolerance)
状态挂了,如何确保状态拥有精确一次(exactly-onceguarantee)的容错保证?就是通过定期的快照+事件日志位置。我们先假设一个简单的场景,如下,一个队列在不断的传输数据。单一的process 在处理数据。这个 process 没处理一个数据都会累计一个状态。如何为这个process 做一个容错。做法就是没处理完一笔,更改完状态之后,就做一次快照(包含它处理的数据在队列中的位置和它处理到的位置以及当时的状态进行对比)
如何在分散式场景下替多个拥有本地状态的运算子产生一个全域一致的快照(global consistent snapshot)?
方式一:更改该任务流过的所有运算子的状态。比较笨,有一个副作用,就是我处理完这笔数据,它应该就到了一个process,我本应该做其他数据的处理了,可是为了全局一致性快照就会停止前面和当前的 process的运算来保证全局一致性。
分散式状态容错
通过 checkpoint 实现分散式状态容错
每一个运算子它本地都有一个维护一个状态,当要产生一个检查点(checkpoit)的时候,都会将这个检查点存储在一个更小的分布式文件系统DFS中。当出现某个算子 fail之后,就会从所有的checkpoint 中获取所有算子的上一个状态进行恢复。把消息队列的位置也进行恢复。也就是多线程工作,每一个任务在DFS中就可以看作一个线程,它们数据存储的key就是这个任务,每一个算子的处理状态都会按照处理顺序添加进去。
分布式快照(Distributed Snapshots)
更重要是时如何在不中断运算的前提下生成快照?其实就是给每一个任务标记一个checkpoint n 不同的任务这个 n是不同的,相同的任务在不同的算子里面它是相同的。具体我们把这个分解后看看。
状态维护(State Management)
本地维护的这个状态可能非常非常大。后端的管理系统一般使用内存维护这些状态。
Event-time 处理
EventTime 是事件产生的时间。
Event-Time 处理
也就是说我们要统计的3-4点之间的数据,程序4点结束这个执行不是根据window时间,而是根据event-Time。
Watermarks
Flink 是watermarks 实现 Event-Time 功能的。在 Flink 里面也属于一个特殊事件,精髓是当某个运算子收到一个带有时间戳t的watermark 后就不会再收到任何小于该时间戳的事件了。也就是当 window需要统计4点的数据时,例如我们每5分钟发一次watermark ,那么当 window收到4.05的watermark 的时候才会去统计4点之前的数据(下一次)。如果4.05收到了4点之前的数据的话,Flink1.5 会把这个事件输出到旁路输出(side output),你可以获取出来,进行处理。目前有一个问题就是:如果某个Stream Partition 没有输入了,也就没有 Watermarks。那么 window 就没办法进行处理了。当多个数据流的 watermarks 不相同的时候,Flink 会取最小的watermarks 进行运算。可以在接收到资源的时候通过代码设置 watermarks。
OutputTag<String> outputTag = new OutputTag<String>("side-output") {};
状态保存与迁移(Savenpoints and Job Migration)
流失处理应用是无时无刻在运行,运维上有几个重要的考量:如何迁移状态
【1】更改应用逻辑/修改bug等,如何将前一个执行的状态迁移到新的上面执行。
【2】如何重新定义运行的平行化程度。
【3】如何升级运算Flink 的版本号。
保存点(SavePoint)
可以想成:一个手动产生的检查点(CheckPoint):保存点记录某一个流失应用中的所有运算中的状态。当触发 SavePoint之后,Flink提供了两种选择停止消费或者继续运算,根据场景定义。