Application注册及Executor启动过程分析

接上一篇”SparkContext初始化流程”,其中在初始化***SchedulerBackend那里提到了,在那里会创建一个AppClient对象,用于和Spark集群交互注册App,申请并启动Executor,本篇就来分析下注册App和启动Executor的流程。

 

先描述下流程:

1. SparkContext启动过程中,AppClient会向Master发送RegisterApplication消息。

2. Master按照FIFO的顺序启动App,通知Worker启动Executor(先确定Executor分配在哪些Worker上),并通知driver应用注册完成

3. Worker接受到Master消息之后,通过ExecutorRunner线程启动Executor所在进程,并向Master汇报ExecutorState.RUNNING,Master再向Driver发送ExecutorUpdated消息。

4. Executor进程启动过程中,首先会发送RegisterExecutor给Driver;Driver记录Executor信息,成功注册会发送RegisteredExecutor给CoarseGrainedExecutorBackend;失败会发送RegisterExecutorFailed消息让Executor进程退出(Driver随后会调用makeOffers()分配运行任务的资源,最后发送LauncherTask消息执行任务)

5. CoarseGrainedExecutorBackend接收到driver注册Executor成功的消息后,真正实例化Executor对象,此时Executor正式启动完毕,会定时向Driver发送心跳。

补充:

6. Driver根据任务结果进行不同的处理,然后给Executor分配新的任务直到结束。

7. Executor启动后,接受从Driver发送的LauncherTask执行任务消息,调用executor.launchTask真正执行任务。

 

Executor启动时序图:

 

此处以standalone为例进行,进行代码层面的分析:

1.AppClient向Master发送RegisterApplication消息

执行StandaloneAppClient.scala中的tryRegisterAllMasters()方法(在driver端执行),因为有HA,所以会遍历所有的Master。找到正在运行的Master后,向其发送RegisterAplication消息。

Master收到消息后,会记录APP以及Driver的一些信息,发送RegistererApplication消息给driver。然后执行Master.scala中的schedule()方法,这个方法是调度集群资源用的,这里的资源个人理解就是Executor。(ps:每次有新的App加入或者Executor资源有变化都会调用一遍该方法)

 

2.Master通知Work启动Executor,并通知driver

Master.scala执行上面提及的schedule()方法,该方法内部会调用startExecutorsOnWorkers()方法,分配资源并启动Executor。(ps:Deploy Mode为cluster的情况下,注册的Driver的信息是放在waitingDrivers中,此时还要在Worker上启动Driver)

按照FIFO顺序逐个启动Application。(这里是从waitingApps中拿数据,也就是说所谓的资源变化调用scheduler(),这个方法不会影响到已经启动的App)

2.1方法首先获取到usableWorkers,即内存和cores都足够的Workers,然后按cores从多到少的顺序进行排列。

2.2执行scheduleExecutorsOnWorkers(),确定在哪些Worker上启动Executor。这里选择Worker是有一定规则的,稍后在下面分析。

2.3最后执行allocateWorkerResourceToExecutors()方法,Master会向Worker发送启动Executor消息,并向driver发送ExecutorAdd消息,让driver完成注册App的流程(standalone模式下,driver端只是打印个日志)。代码如下:

 

上面2.2中的ScheduleExecutorsOnWorkers()有两种分配的模式:一种是将Executor尽可能多的分配到Worker上,而另一种是将Executor尽可能少的分配到Worker。

默认都是使用前一种。(ps:如果Worker上Executor的数量比coresPerExecutor的少,那么executors就不会启动.(1.4之后就不是这样每次一个core这样分了,而是一次性corePerNode分完,所以1.4之后不会有这样的问题))

 

3.Worker接受到Master消息之后,通过创建一个叫ExecutorRunner线程启动Executor所在进程,并向Master汇报ExecutorState.RUNNING

从代码中可以看出,Worker首先创建了一个ExecutorRunner线程,线程真正启动Executor进程。(这么设计应该是为了让该线程可以kill掉Executor进程,然后进行资源的回收么?这一步意义还不是很理解)

启动Executor是使用了进程生成器ProcessBuilder,它的start()方法会以System.Runtime的方式启动一个子进程,这个进程的类名是CoarseGrainedExecutorBackend,它是Executor的容器,启动就是执行它的main()方法。

(ps:类名的Command的信息是在***schedulerbackend对象中就拼接好了的)

 

4.Worker上真正启动Executor进程

启动进程时会实例化CoarseGrainedExecutorBackend并向RpcEnv进行注册,注册时会执行它的onStart()方法,向Driver发送RegisterExecutor消息。

成功注册的话,Driver会向Executor发送RegisteredExecutor消息。Driver端的***SchedulerBackend接收到消息后会记录一些信息,然后发送RegisteredExecutor消息给Executor。(如果发送RegisterExecutorFailed,进程退出)

CoarseGrainedExecutorBackend在接受到消息后会创建一个Executor对象准备任务的执行,至此Executor才算真正启动完毕。Executor会保持和driver的心跳,接下来就是等待任务的调度与执行了。

 

Worker上启动Executor进程的整个过程图示如下:

 

启动Executor完整流程图为:

 

 

下一篇会分析下Job具体执行的流程,这里先按照书上的内容先梳理下,晚点再调试源码:

5.driver端接收到RegisterExecutor消息之后的操作

Driver端向Executor端发送RegisteredExecutor消息之后,会调用makeOffers()执行任务。

makeOffers()会将任务随机分发给各个Executor,这样做是为了负载均衡,并且方法内部会参考数据本地性。最后将任务信息进行包装,发送LaunchTasks信息到Executor。

 

 

6.Executor是如何执行Task的

CoarseGrainedExecutorBackend收到消息后会执行Task。

每个Task会被封装成一个TaskRunner对象,然后提交到Executor的线程池中执行。

Task执行成功后,回收该Executor运行改任务的CPU数,然后根据任务重新分配。

 

参考:

https://www.jianshu.com/p/0b19b2b954b6(Executor启动流程)

https://blog.csdn.net/u011564172/article/details/69922241(Executor进程如何启动)

《图解Spark》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值