Spark任务提交后的处理流程

以yarn-cluster(顺带说下client模式)为例,包名org.apache.spark用oas代替,org.apache.hadoop用oah代替。org.apache.spark.deploy.yarn用sdy代替。org.apache.hadoop.yarn.client.api用hyca代替。

常用类如下:私有类:sdy.ApplicationMaster;sdy.ExecutorRunnable;sdy.YarnAllocator;oas.scheduler.cluster.CoarseGrainedSchedulerBackend;scala.concurrent.impl.Promise。

下面是Spark提交的流程:
SparkSubmit#main->doSubmit:判断submit/kill等,这里是submit。
doSubmit->submit:参数为appArgs,调用prepareSubmitEnvironment准备环境和配置。
submit->runMain:创建SparkApplication对象,yarn-cluster创建其子类oasdy.YarnClusterApplication对象app。
app.start:调用new Client(<args, sparkConf>).run()。new时做了很多对象初始化,如用hyca.YarnClient创建其实现类hyca.impl.YarnClientImpl的实例yarnClient。
run->submitApplication:准备和Yarn的连接通讯初始化等,包括创建YarnClientApplication对象newApp,用来获取AppId,还创建ContainerLaunchContext(父类ContainerContext)实例containerContext(内部包含AM类参数sdy.ApplicationMaster,client模式为sdy.ExecutorLauncher,内部包含了启动AM的命令,命令内容如前代码块),将newApp嵌入到newAppResponse对象,后者嵌入到containerContext对象,containerContext又嵌入到创建的ApplicationSubmissionContext对象appContext中。
yarnClient.submitApplication:上述的appContext是参数,使用ApplicationClientProtocol实例rmClient(yarnClient创建时就初始化了该对象)和RM通讯来提交应用。提交之后,yarnClient处于循环监听状态。

下面是AM端的运行流程:
yarnClient提交之后,YARN会运行提交的命令,也就是启动AM,入口是ApplicationMaster伴生对象的main方法。ApplicationMasterArguments类对args进行解析,然后创建ApplicationMaster伴生类的实例master,同时将解析后的amArgs作为master初始化的参数,实例内使用doAsUser用户创建YarnRMClient对象client成员,还有SparkConf对象sparkConf,YarnConfiguration对象yarnConf等,接着执行master#run()方法启动ApplicationMaster。
ApplicationMaster属性解释:
–isClusterMode:是否cluster模式,userClass有值则为true,来自ApplicationMaster参数–class '<用户类>'的值。
–sparkConf:spark运行配置,配置信息来自args.propertiesFile,来自ApplicationMaster的参数–properties-file的值。
–securityMgr:安全管理类,初始化时需要传入sparkConf对象。
–metricsSystem:是测量系统初始化,用来记录执行进度,资源等信息。
–client:是YarnRMClient的对象,它主要用来使用YARN ResourceManager处理注册和卸载application。对应类为sdy.YarnRMClient。
–userClassLoader:用户类加载器,从sparkConf中获取application的jar路径,此ApplicationMaster实例初始化接收参数,为后边通过它的main运行做辅助。
–userClassThread:用来执行application main的线程。
–rpcEnvrpc:通信环境对象
–sparkContextPromise:在cluster模式,当用户(application)的SparkContext对象已经被初始化用来通知ApplicationMaster。
master.run->master.runImpl:前面是System.exit(master.run())。判断cluster模式,是则执行runDriver方法,否则执行runExecutorLauncher方法。这里是runDriver。

runDriver:初始化一个用户线程userClassThread=startUserApplication(),运行用户定义的代码,通过反射运行sparksubmit命令中–class指定的类的main函数。这说明Driver运行在AM上。接着初始化SparkContext,通过sparkContextPromise来获取用户程序初始化的SparkContext对象sc(线程等待用户应用线程初始化sc完成),并设定sc最大等待时间。resumeDriver()表示初始化sc完成后恢复用户线程(拿到sc后似乎要让用户线程等待一下,等待着这里注册和设置,到该方法执行表示等待结束)。接下来阻塞当前线程知道用户的main方法执行结束。

SparkContext初始化(Driver内):初始化时,master为url时先创建ExternalClusterManager的实现类实例cm,这里实现类暂时只有YarnClusterManager,yarn-cluster模式时,使用cm创建TaskScheduler的实现类TaskSchedulerImpl的子类YarnClusterScheduler的实例作为TaskScheduler(包裹sc对象),同时创建YarnClusterSchedulerBackend(继承自CoarseGrainedSchedulerBackend)的实例作为SchedulerBackend(包裹sc)。YarnClusterScheduler覆写了postStartHook方法,这是一个后启动钩子,执行时会执行ApplicationMaster.sparkContextInitialized(sc)把SparkContext实例赋给客户端等待获取sc的Promise对象,此时用户线程等待,等待客户端收到sc继续设置和获取sc中的相关参数,之后客户端唤醒用户线程。唤醒客户端线程之前,AM(暂理解runDriver方法中是AM,用户线程运行的是Driver)从sc获取到userConf(SparkConf类型),driver host,driver port,ui,接着使用这些参数调用registerAM方法向RM注册AM,该方法传入host,port,historyAddress再调用client#register,register内部过程是:创建hyca.AMRMClient实例amClient,实例内初始化yarnConf(YarnConfiguration);接着调用amClient#start方法启动AM客户端;然后同步调用amClient#registerApplicationMaster方法,传递host,port和tackingUrl。该过程完成向RM注册AM。

runDriver续(AM内):将AM注册到RM后,创建driverRef(driver的EndpointRef),用于AM与Driver通讯。Container启动Executor时也传递了driverRef的信息,Executor就可根据该信息获取到driverRef实现Executor与Driver的RPC通讯。接下来在createAllocator(driverRef, userConf)内的过程是:调用client#createAllocator创建分配器(配置器,也就是向RM申请Container资源)对象allocator(YarnAllocator);接下来创建AMEndpoint(ApplicationMaster内部类,本质是RpcEndpoint,创建时传递了driverRef)对象用来和driver的RPC通讯;然后allocator#allocateResources循环申请Container,然后使用ExecutorRunnable启动Executor,一个Container启动一个Executor。

AMEndpoint内:上步中创建的AMEndpoint对象的onStart方法内使用driver(指传递的driverRef,猜测指SparkContext对象或者包含)发送RegisterClusterManager对象,用来告知driver,ClusterManager权限交给AMEndpoint,Driver端用YarnSchedulerBackend的子类YarnClusterSchedulerBackend对象接收。对象内的receiveAndReply方法的任务是:RequestExecutors请求分配executor;KillExecutors杀掉所有executor;GetExecutorLossReason获取executor丢失原因。

allocator#allocateResources:先调用amClient(AMRMClient实例)#allocate方法(内部再调用rmClient#allocate,这里rmClient是ApplicationMasterProtocol实现类对象)。
->handleAllocatedContainers->runAllocatedContainers:用ExecutorRunnable类创建实例er用来启动Executor,本质是启动一个CoarseGrainedExecutorBackend进程,ExecutorRunnable中封装了该进程启动脚本。然后运行er#run方法。

er#run(AM内):首先创建NodeManager客户端nmClient(NMClientImpl实例),然后运行nmClient#init(yarnConf)和nmClient#start(该方法内部做了状态判断,运行了serviceStart方法,但该方法为空),接着运行startContainer方法,主要工作都在该方法内完成,该方法调用过程是:先调用prepareEnvironment方法准备Executor运行环境对象env和prepareCommands方法准备启动CoarseGrainedExecutorBackend进程的脚本对象commands,然后将这两个对象放入ContainerLaunchContext对象ctx中,然后调用nmClient#startContainer,同时将Container选项参数和ctx传入。

nmClient#startContainer(AM内):向RM申请Container并在该Container中运行ctx的命令来启动oas.executor.CoarseGrainedExecutorBackend(私有类)进程,该进程启动后就是对应的Executor。

下面是CoarseGrainedExecutorBackend(本质是RpcEndpoint)的启动过程:
和SparkSubmit半生对象、AppllicationMaster半生对象一样,CoarseGrainedExecutorBackend也包含一个半生对象,这三个都是命令方式启动,都包含一个入口main函数,main函数解析ExecutorRunnable传入的参数如下:
a) var driverUrl: String:driver的Rpc通信Url
b) var executorId: String:executor的编号id(一般driver所在executor编号为0,其他一次加1,连续的)
c) var hostname: String:executor运行的集群节点的hostname
d) var cores: Int:executor可使用vcore个数
e) var appId: String:当前应用程序的id
f) var workerUrl: Option[String]:worker UI地址
g) val userClassPath = new mutable.ListBufferURL:用户代码(当前应用程序)main所在的包和依赖包的路径列表。
接下来将解析后的参数传入并调用run方法,该方法过程如下:
1)通过driverUrl与driver Endpoint建立通信,向driver需求Spark应用程序的配置信息,并来创建driverConf对象。RetrieveSparkAppConfig类型请求被driver的schedulerBackend属性接收。
2)通过SparkEnv.createExecutorEnv()方法创建SparkEnv对象env,SparkEnv#createExecutorEnv内部会创建以下几类组件:RpcEnv,securityManager,broadcastManager,mapOutputTracker,shuffleManager,memoryManager,blockTransferService,blockManagerMaster,blockManager,metricsSystem,outputCommitCorrdinator,outputCommitCoordinatorRef等。
3)通过env.rpcEnv对象开放RPC通信接口“Executor”,对应RpcEndpoint类型是CoarseGrainedExecutorBackend类。
4)通过workerUrl开发RPC通信接口“WorkerWatcher”,用来监控worker运行。WorkerWatcher的功能:连接到工作进程并在连接断开时终止JVM的端点;提供工作进程及其关联子进程之间的命运共享。
5)调用env.rpcEnv.awaitTermination()来阻塞程序,直到程序退出。

下面是CoarseGrainedExecutorBackend伴生类内部的运行过程:
onStart:重写RpcEndpoint的onStart()方法,对应Executor启动后调用该方法。方法内先调用rpcEnv.asyncSetupEndpointRefByURI(driverUrl)根据driverUrl异步的方式获取driverEndpointRef并赋值给drvier属性;接着调用RegisterExecutor(executorId, self, hostname, cores, extractLogUrls)发送信息到driver的schedulerBackend,该实例是CoarseGrainedSchedulerBackend的子类的类型,driver接收到该信息后会调用schedulerBackend#driverEndpoint#receiveAndReply(context: RpcCallContext)做出响应,receiveAndReply方法内部拿到了executorRef,并使用它发送信息executorRef.send(RegisteredExecutor)给executor,接着CoarseGrainedExecutorBackend的receive方法将接收到并处理,也就是executorBackend(也就是executorRef)和schedulerBackend做收发消息后的处理。

receive:覆写RpcEndpoint的receive方法。RegisterExecutor接收到driver端已经注册了executor(注册时driver保留executorId,executorAddress等信息),此时才在executor端调用executor = new Executor(executorId, hostname, env, userClassPath, isLocal = false)进行executor启动,executor主要负责执行task,上报task执行状态,进度,资源占用情况等。下面是方法内接收到其他的内容:

  1. RegisterExecutorFailed(message) 注册executor失败。
  2. LaunchTask(data) 加载任务,通过executor去执行 executor.launchTask(this, taskDesc)。
  3. KillTask(taskId, _, interruptThread, reason) 杀掉task任务。
  4. StopExecutor 停止executor。
  5. Shutdown 关闭executor。
  6. UpdateDelegationTokens(tokenBytes) 更新代理token。
    上边这些参数类型定义在CoarseGrainedClusterMessages中,这些接收到的消息发送者是driver端SparkContext下的schedulerBackend(CoarseGrainedSchedulerBackend)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值