Spark
Spark的部分内容和之前Spark系列重合,但是有些细节之前没有深入。
Master启动
基于高可用的考虑,Master会启动多个,但是只能有一个Master是提供服务的(ALIVE状态),其他Master都是STANDBY状态。
zookeeper集群的左右,一个是用来选举,另外一个是保存持久化Application、Driver、Executor的信息,便于恢复。
Worker启动
Worker在启动的时候,就会向Master进行注册,注册时需要上传workid,host,端口以及cup核心数,内存大小。
由于Master是高可用的,所以刚开始Worker并不知道ALIVE节点是哪个,于是他就会把注册请求发送给每一个Master节点。
Worker只有注册成功,才会被Master调用,所以为了保证注册成功,Worker会进行不断的尝试。
一共尝试的次数是16次,为了避免所有的Worker同一时间发送心跳,给Master造成压力,所以发送心跳的间隔时间是随机的。
前面6次的心跳间隔时间是5~15s之间,后面10次的心跳间隔时间是30~90s之间。
如果在这16次内注册成功,那就会取消尝试。如果16次都没成功,那这个Worker也没必要启动了,直接退出Worker进程。
这里重试的和刚开始注册的时候,有些不同。有可能因为某些原因导致重试,但是这个时候Worker是有Master的ALIVE节点信息,那Worker注册的时候,就直接往ALIVE节点注册就好了,不用每个Master都发送注册请求。
Master接收注册时,会看看自己是不是ALIVE节点,如果是STANDBY节点,那直接跟Worker说我是STANDBY节点,Worker知道他是STANDBY节点,就不做任何处理。
然后Master就会看这个Worker是否注册过了(根据worker注册提供的workid),如果注册过了,就跟Worker说,注册失败了。
Worker接收到注册失败的信息,就会看看我有木有注册成功啊(注册成功会有变量保存),如果注册成功,就忽略这条消息,知道自己是重复注册。
如果发现自己没注册成功,Master也说没注册成功,那就是没注册成功,所以退出Worker进程。
如果既不是STANDBY节点,Worker也没注册过,那就保存Worker的相应的信息,进行持久化,然后告知Worker已经注册成功。
Worker接收到成功后,就会把变量更改为注册成功(上面判断有用到),然后记录Master的地址(后面请求直接发这个地址了),取消注册的重试任务(已经成功了就不需要再尝试注册了)。
最后会发状态给Master,由于刚开始注册,Worker中并没有Driver和Executor,所以Master不会处理。
Master如何知道Worker存活
Worker注册成功后,还有一个非常重要的事情,就是发送心跳,维持状态。发送心跳的时候,直接发送workerid就好。
Master接收请求后,先看看是否注册过,如果没有注册过,就会让Worker重新注册,就会重复上面的注册流程。如果注册过,就修改Worker最后的心跳时间。
Master会有一个每隔60s的定时任务,对超过60s没有发送的Worker进行处理,会把这个Worker标记为DEAD状态,并移除其他相关内存(idToWorker用于映射workerid和Worker的关系,addressToWorker用于映射地址和Worker的关系)。
如果Worker已经是DEAD状态了,那超过960s就把Worker信息也移除。
比如60s没发心跳,此时Master会移除相关内容,然后在960s内,Worker重启后进行注册,那就会把Worker中为DEAD状态的Worker删除,再重新加新的Worker信息。
Driver启动
Driver在启动后需要向Master注册Application信息,和Worker注册Master一样,Driver也不知道哪个是ALIVE节点,所以他也向所有的Master进行注册。
注册信息包括Application的名称,Application需要的最大内核数,每个Executor所需要的内存大小,每个Executor所需的内核数,执行Executor的命令等信息。
这里的注册也有重试次数,最大重试3次,每次间隔20s,注册成功后,就会取消重试。
Master收到请求后,如果是STANDBY节点,那不做处理,也不回复任何信息(这个和Worker注册不一样,Wokrer那边回回复信息,但是Worker不处理)。
如果不是STANDBY节点,那就会把Application信息保存内存中,并做持久化。
接着就会给Driver发送已经注册成功的消息,Driver接收到消息,就会记录Master的信息,以及内部标识已经注册成功,不需要再重试。
Master如何知道Driver存活
Driver和Master之间并没有心跳,不像Worker会定时发送心跳,Master根据心跳移除过期的Worker,那Master怎么知道Driver是否退出呢?
第一种方式是Driver主动告知Master,第二种方式是Driver不正常退出,Master一旦监听到Driver退出了。这两种方式都会取消Application的注册。
Master接收到取消注册Application的消息后,就会移除缓存的Application及Application相关的Driver信息。
对于Driver,有可能还存在运行的Executor,就会发消息给Driver,让他杀死Executor,Driver收到消息后就会停止Application。
对应Worker,Master会群发给所有的Worker,告知这个Application已完成,Worker收到消息后,会清理Driver的临时文件。
最后把Application的信息持久化,并且告知其他Worker这个Application已完成。
Executor启动
Master在资源调度的时候,会让Worker启动Executor。
Worker接收到消息后,会判断是否是STANDBY节点的Master发送的消息,如果不是则忽略。
如果是,Worker就会创建一个线程,用来启动一个Executor的进程,启动Executor的进程后会回复Master说Executor已经启动成功。
Master知道Executor启动成功,也会告知Driver,你的Executor我已经帮你启动了。由于Executor并没有结束,Driver并没有做其他处理。
Executor启动后,就会向Driver进行注册,Driver先判断是否已经注册过或者在黑名单里,如果是,返回失败,如果不是,则保存这个Executor的信息,并告知Executor已经注册成功。
Driver在Executor注册时,还做了一件事,就是把注册信息发送给事件总线。Driver里还有有一个心跳接收器,用于管理Executor状态的。
这个心跳接收器会对总线的事件进行监听,当发现有Executor新增的时候,就会记录这个Executor的id和时间。
Worker如何知道Executor存活
Worker创建线程用来启动Executor进程的时候,这个线程创建完并不会直接退出,而是一直等待获取Executor进程的退出状态。
获取后就会把状态发送给Worker,Worker再把状态转发给Master,并更改自身的内存、CPU信息。
Master发现Executor执行完了(不管失败还是成功),就会更新内存信息,并且把状态转发给Driver。
Driver收到状态后,发现Executor执行完了,会发移除Executor的事件给事件总线。
心跳接收器会对总线的事件进行监听,当发现有Executor移除的时候,就会移除Executor。
Executor如何知道Worker存活
Executor有一个WorkerWatcher,当Worker进程退出、连接断开、网络出错时,就会被WorkerWatcher监听到,进而终止Executor的进程。
Driver如何知道Executor存活
Executor收到Drvier注册成功的消息后,就开始创建Executor对象,这个对象实例化后,就会开始对Driver的心跳请求,由于可能会多个Executor启动,所以为了避免同一时间请求过多,这里的延时时间会加一个随机值。
心跳接收器接收到心跳请求后,先看看这个Executor是否已经注册了,如果没有,让Executor重新注册,如果注册过了,则更新时间。
心跳接收器有一个定时任务,会扫描每个Executor最后上报的时间,如果Executor已经超过一定时间没有发心跳了,就会把这个Executor的信息从内存中移除,并且提交“杀死”Executor的任务。
这个任务最后会发送到ClientEndpoint,ClientEndpoint再转发给Master。