简单介绍
Flink时apache的顶级项目,使用java语言编写!(在这个scala泛滥的时代还真是令人感动呢)
支持流处理,也支持批处理。Flink最大的原则就是,以流处理为根本。Flink认为批处理应该在流处理上实现。通过插入barrier事件来标记微批,收到该事件后,进行checkpoint快照。发现处理失败,就根据快照恢复然后进行流重放。
flink的checkpoint机制非常轻量,barrier不会打断streaming的流动,而且做checkpoint操作也是异步的。其次,相比storm需要ack每条data,flink做的是相当于small batch的checkpoint,容错的代价相对要低很多。最重要的是flink的checkpoint机制能保证exactly once。(装作Trident不存在一脸无辜的我)。
Flink 架构
JobManager 主要负责调度 Job 并协调 Task 做 checkpoint,职责上很像 Storm 的 Nimbus。从 Client 处接收到 Job 和 JAR 包等资源后,会生成优化后的执行计划,并以 Task 的单元调度到各个 TaskManager 去执行。
TaskManager 在启动的时候就设置好了槽位数(Slot),每个 slot 能启动一个 Task,Task 为线程。从 JobManager 处接收需要部署的 Task,部署启动后,与自己的上游建立 Netty 连接,接收数据并处理。
对于相同机器之间的task通信
对于不同task机器通信
Flink On Yarn
-
FlinkYarnSessionCli 启动的过程中首先会检查Yarn上有没有足够的资源去启动所需要的container,如果有,则上传一些flink的jar和配置文件到HDFS,这里主要是启动AM进程和TaskManager进程的相关依赖jar包和配置文件。
-
接着yarn client会首先向RM申请一个container来启动ApplicationMaster(YarnApplicationMasterRunner进程),然后RM会通知其中一个NM(node manager)启动这个container,被分配到启动AM的NM会首先去HDFS上下载第一步上传的jar包和配置文件到本地,接着启动AM;在这个过程中会启动JobManager,因为JobManager和AM在同一进程里面,它会把JobManager的地址重新作为一个文件上传到HDFS上去,TaskManager在启动的过程中也会去下载这个文件获取JobManager的地址,然后与其进行通信;AM还负责Flink的web 服务,Flink里面用到的都是随机端口,这样就允许了用户能够启动多个yarn session。
-
AM 启动完成以后,就会向AM申请container去启动TaskManager,启动的过程中也是首先从HDFS上去下载一些包含TaskManager(yarn模式的话这里就是YarnTaskManager )主类 的jar和启动过程依赖的配置文件,如JobManager地址所在的文件,然后利用java cp的方式去启动YarnTaskManager ,一旦这些准备好,就可以接受任务了。这个和spark on yarn的yarn cluster模式其实差不多,也是分为两个部分,一个是准备工人和工具(spark是启动sc的过程,flink是初始化ENV的过程),另外一个就是给工人分配具体工作(都是执行具体的操作,action什么的触发)
Chandy-Lamport算法(一种分布式快照算法)
算法是有前提的,就是默认节点永远可以正确处理网络通信(节点不崩溃,网络不中断,通信不乱序,等等)根据CAP,如果我们不需要保证P,那么我们肯定可以找到一种高可用而且一致性的方案。当然,既然要高可用,就一定不能依赖全局的共享存储和全局时钟。这里的所谓一致性,也是强一致性的概念,系统时时刻刻保持一致性。
原paper的例子是两个节点传递令牌,任何时候,令牌只存在于P,Q两个节点或两个节点之间的通信C和C’中。
每个节点都会记录自己的state,和所有自己的incoming channel的state。
算法总体流程如下
- 任何节点的snapshot由本地状态snapshot和节点的input channel snapshot组成
- 任何src可以任意时间决定take本地状态snapshot,take完本地snapshot,广播一个marker给所有下游
- 任意没有take本地snapshot的节点(注意这个算法里src也是可以接受别人的msg的),假设从第x个channel收到第+ 一个marker的时候,take本地状态snapshot(且take接受到第一个marker的input channel-x的channel snapshot为空),然后给所有output channel广播这个marker
- 从收到第一个marker并take完本地snapshot之后,记录所有input channel的msg到log里,直到从所有的input channel都收到这个marker. 作为这些input channel的channel snapshot。
在任何时候系统崩溃,所有src广播maker前发送事件在分布式系统内都是可以被恢复的。系统崩溃后,各节点加载自己的状态的SNAPSHOT,然后src重新发送maker就行。(防止在src记录snapshot到发送maker之间系统崩溃。
A,B,C,D代表4个process.有向线段代表FIFO的channel.绿色圆形代表普通message,橙色矩形代表marker.蓝色的节点和线段代表已经记录state的process和channel
Process A启动snapshot算法,A执行marker sending rule(记录自身state,然后发送marker):
Process B接收到marker,执行marker receiving rule:将channel AB的state记为空集,然后记录自身state并向下发送marker:
Process C接收到marker, 执行marker receiving rule:将channel AC的state记为空集,然后记录自身state并向下发送marker:
Process D接收到来自于process B的marker, 执行marker receiving rule:将channel BD的state记为空集,然后记录自身state并向下发送marker:
Process D接收到来自于process C的marker, 执行marker receiving rule:这是process D第二次接收到marker,将channel CD的state记为{5},不会向下发送marker:
自此process A,B,C,D的local state和所有Channel的state都记录完毕. 将这些local state组合,所得到的就是global state
节点在不断发消息的同时做local snapshot和发送marker(Flink中称为barrier个人觉得更加直观),然后所有节点都处理marker前的数据,marker后的数据先缓存记录,等所有节点都处理完marker前的数据并且都导出自己的local snapshot后,这个global snapshot就完成了,而且保证所有节点都处理完marker前的数据。
Flink的算法 ---- Asynchronous Barrier Snapshotting(ABS)
每个节点都要记录所有income channel state。这种做法有好处,就是事件可以随到随处理。但是也有不好的地方,那就是每个节点都要记录income channel state和自己的state,我们要得知目前系统的状态,就必须查询节点的记录。然而,在实际生产中,我们希望的是在sink端就可以得知目前系统的状态。所以,Flink做了一点改变。
A是JobManager, B C是source,D是普通task。JobManager发起一次snapshot:向所有source发送barrier.
每个Barrier先后到达各自的source.Source在收到barrier后记录自身state,然后向下游节点发送barrier
Barrier (from)B 到达process D,但不会进行snapshot
Barrier (from)B已经到达process D,
所以当来自于channel BD的record 6 7到达后,process D不会处理它们,而是将它们放入input buffer.
而Barrier (from)C尚未到达process D,所以当来自于channel CD的record 4到达后,process D会处理它.
Barrier C也到达process D.
这样,process D已经接收到了所有上游process的barrier.process D记录自身state,然后向下游节点发送barrier
如果上述过程中不准备input buffer而是对6,7事件进行直接处理,那么就是实现了At Least Once语义。因为恢复时系统无法判定6,7是否被处理,所以选择进行重发。
Flink中端到端的Exactly Once实现
上述算法只能Flink内部在任何时候状态都是一致的。但是,如果涉及到外部系统,问题则依然存在。
上图中,如果下面的路径处理失败,但是SinkA已经把offset提交到外部系统了。这是不可能回滚的。
2PC还是如期而至了
- Phase 1: Pre-commit
Flink的JobManager向source注入checkpoint barrier以开启这次snapshot.
barrier从source流向sink.
每个进行snapshot的算子成功snapshot后,都会向JobManager发送ACK.
当sink完成snapshot后, 向JobManager发送ACK的同时向kafka进行pre-commit. - Phase 2:Commit
当JobManager接收到所有算子的ACK后,就会通知所有的算子这次checkpoint已经完成.
Sink接收到这个通知后, 就向kafka进行commit,正式把数据写入到kafka
Flink CEP
CEP(Complex Event Processing)就是在无界事件流中检测事件模式,让我们掌握数据中重要的部分。flink CEP是在flink中实现的复杂事件处理库。
一个结束
https://www.jianshu.com/p/2e4caf0d2cd7?utm_campaign=shakespeare
WaterMark
Flink在流处理程序支持不同的时间概念。分别为Event Time/Processing Time/Ingestion Time,也就是事件时间、处理时间、提取时间。
从时间序列角度来说,发生的先后顺序是:
-
事件时间(Event Time)----> 提取时间(Ingestion Time)----> 处理时间(Processing Time)
- Event Time 是事件在现实世界中发生的时间,它通常由事件中的时间戳描述。
- Ingestion Time 是数据进入Apache Flink流处理系统的时间,也就是Flink读取数据源时间。
- Processing Time 是数据流入到具体某个算子 (消息被计算处理) 时候相应的系统时间。也就是Flink程序处理该事件时当前系统时间。
窗口触发机制
假如我们设置10s的时间窗口(window),那么0~10s,10~20s都是一个窗口,以0~10s为例,0为start-time,10s为end-time。假如有4个数据的event-time分别是8(A),12.5(B),9©,13.5(D),我们设置Watermarks为当前所有到达数据event-time的最大值减去延迟值3.5秒
当A到达的时候,Watermarks为max{8}-3.5=8-3.5 = 4.5 < 10,不会触发计算
当B到达的时候,Watermarks为max(12.5,8)-3.5=12.5-3.5 = 9 < 10,不会触发计算
当C到达的时候,Watermarks为max(12.5,8,9)-3.5=12.5-3.5 = 9 < 10,不会触发计算
当D到达的时候,Watermarks为max(13.5,12.5,8,9)-3.5=13.5-3.5 = 10 = 10,触发计算
常见实践
- 采用系统时间减去一定时间
- 采用Event Time减去一定时间做watermark