Spark消息通信原理(二)——Spark启动消息通信

        Spark启动过程中,主要是进行Master和Worker之间的通信。

 

        首先,由Worker节点向Master发送注册信息,然后,Master处理完毕,返回注册成功或者失败消息,如果注册成功,Worker会定时发送心跳给Master。

具体过程如下:

        当master节点启动后,随之启动各worker节点,worker启动时会创建RpcEnv,以及EndPoint终端,并向master发送注册worker的消息RegisterWorker。(上一篇文章也提到过,先启动master,再启动worker,因为worker的数量根据实际任务来决定)

(1)worker节点向master节点请求注册
        在worker中有一个tryRegisterAllMasters方法,里面会创建一个注册线程池(因为一个worker可能需要注册到多个master中,如HA环境),将注册请求放进线程池中,通过线程池启动注册线程(即并发的进行注册请求)。tryRegisterAllMasters部分源码:

private def tryRegisterAllMasters():Array[JFuture[_]] = {
    masterRpcAddress.map{ masterAddress =>
        registerMasterThreadPool.submit(new Runnable{
            override def run(): Unit = {
                try{
                    logInfo("Connecting to master " + masterAddress + "......")
                    //获取master终端引用
                    val masterEndPoint = rpcEnv.setupEndPointRef(masterAddress, Master.ENDPOINT_NAME)
                    //注册信息
                    registerWithMaster(masterEndPoint)
                }catch{......}
 
......
 
}

        上述源码里面,可以看到是遍历masterRpcAddress,一个worker向每个master都发送一条注册信息请求,但这个请求并不是马上执行,而是放在线程池中。registerWIthMaster方法则是对注册请求的处理,registerWIthMaster方法部分源码:

private def registerWithMaster(masterEndPoint:RpcEndpointRef):Unit = {
    //根据Master终端引用,发送注册信息
    masterEndpoint.ask[RegisterWorkerResponse](RegisterWorker(workerId,host,port,self,cores,memory,workerWebUiUrl))
    .onComplete{
        //RegisterWorker处理之后会有两种结果,一个是Success,另一个是Failure。根据结果,进行不同的后续处理。
        case Success(msg) => Utils.tryLogNonFatalError{handleRegisterResponse(msg)}
        case Failure(e) => logError(s"Cannot register with master: ${masterEndpoint.address}", e)
        System.exit(1)    //退出
    }(ThreadUtils.sameThread)
}

(2)Master节点处理处理来自Worker节点的注册请求
        master收到消息后,需要对worker发送的信息进行验证、记录。如果注册成功,则发送RegisterWorker消息给对应的worker,worker接收到成功信息,则定期发送心跳信息给master;如果注册失败,则会发送RegisterWorkerFailed消息,Worker打印出错日志并结束Worker启动。上面的RegisterWorker源码如下:

case RegisterWorker(id, workerHost, workerPort, workerRef, cores, memory, workerWebUiUrl) =>{
    if(state == RecoveryState.STANDBY){
        //如果master处于standby状态,返回“Master处于StandBy状态”信息
        context.reply(MasterInStandby)
    }else if(idWorker.coontains(id)){
        //如果列表中已经存在该worker信息,则返回RegisterWorkerFailed信息
        context.reply(RegisterWorkerFailed("Duplicate worker ID"))
    }else{
        val worker = new WorkerInfo(id, workerHost, workerPort, cores, memory, workerRef, workerWebUiUrl)
        //registerWorker(worker)用于将worker放到列表中
        if(registerWorker(worker)){
            //放入列表成功
            persistenceEngine.addWorker(worker)
            context.reply(RegisteredWorker(self, masterWebUiUrl))    //心跳发送
            schedule()
        }else{
            //放入列表失败
            val workerAddress = worker.endpoint.address
            context.reply(RegisterWorkerFailed("Attempted to re-register worker at same address: "+ workerAddress))
        }
    }
}

(3)Worker节点注册成功后的心跳通信
        当worker节点接收到注册成功信息时,会进行两个操作:1、记录日志并更新master信息;2、向master发送worker的各个executor的最新状态信息,以及定时发送心跳信息。心跳时间可以通过spark.worker.timeout设置,这个是worker“罢工”时间间隔,即判断worker存活的时间,而心跳时间则是该值的1/4。

private val HEARTBEAT_MILLS = conf.getLong("spark.worker.timeout",60) * 1000 / 4

心跳发送RegisteredWorker(self, masterWebUiUrl)的部分源码如下:

case RegisteredWorker(masterRef, masterWebUiUrl) =>{
    loginInfo("Successfully registered with master" + masterRef.address.toSparkURL)
    registered = true
    changeMaster(masterRef, masterWebUiUrl)     //更新worker所持有的master信息
    //定时发送的心跳
    forwordMessageScheduler.scheduleAtFixedRate(new Runnable{
            override def run():Unit = Utils.tryLogNonFataError{
                self.send(SendHearbeat)
            }
        }, 0, HEARTBEAT_MILLS, TimeUnit.MILLISECONDS)
    //如果设置清理以前应用使用的文件夹,就进入该逻辑
    if(CLEANUP_ENABLED){
        ...
    }
 
    //获取worker中各个Executor的最新状态
    val execs = executors.values.map{
        e=>new ExecutorDescription(e.appId, e.execId, e.cores, e.states=)
    }
    //向master汇报worker中各个Executor的最新状态
    masterRef.send(WorkerLatesState(workerId, execs.toList, drivers.keys.toSeq))
}

总结:Spark的Worker启动消息通信过程

        worker并发启动注册请求线程——>对应的master接收到请求——>master处理请求:若注册失败,返回RegisterMasterFailed提示信息;若注册成功,发送RegisteredMaster提示信息——>worker接收返回信息:若是RegisterMasterFailed信息,则结束线程;若是RegisteredMaster信息,则向master发送executors状态,以及定时发送心跳信息。

posted @ 2019-08-13 13:21  KamShing  阅读(372)  评论(0编辑  收藏  举报