spark下的Task分发


文章目录

  • spark下的Task分发
  • 前言
  • 一、Inbox类
  • 1.1 process方法。
  • 1.2 post方法。
  • 二、Dispatcher类
  • 2.1 构造器与成员变量
  • 2.2 postMessage方法
  • 2.3 MessageLoop方法
  • 三. outBox类
  • 3.1主要成员变量
  • 3.2主要的内部类
  • 3.2send方法
  • 3.2drainOutbox方法
  • 四. CoarseGrainedExecutorBackend
  • 3.1 onStart()方法
  • 3.2 receive方法
  • 五.CoarseGrainedSchedulerBackend
  • 5.1调度接口
  • 5.2重要的成员变量
  • 5.2.1executorDataMap
  • 5.2.2 DriverEndpoint
  • 5.2.2.1 onStart方法
  • 5.2.2.2 recieve方法
  • 5.2.2.3 分发任务给executor
  • 5.2.2.4 launchTasks
  • 六 NettyRpcEndpointRef
  • 6.1send方法
  • 七 NettyRpcEnv
  • 7.1重要的成员变量
  • 7.2 Send方法
  • 7.2 postToOutbox方法
  • 7.3 executor执行分发过来的task
  • 结尾


前言

1.不用多说,继续学习任务调度的分发流程O(∩_∩)O。前期学习netty目标也是为了理解spark这一块通信的内容。
2.从边缘地带一步步向中心大陆进军~~。

一、Inbox类

1.inbox就是用来存储inboxmessage以及将该message进行发送出去的类。大神代码写的太优雅了/(ㄒoㄒ)/~~ 。在该构造器中着重关注的对象当然是LinkedList对象了,它装载的就是待发出去的信息,一般都是发到本地的。
2.每一个Inbox都存储了用来处理该message对应的endpoint以及endpointRef引用对象。

sparkdataset怎么分批处理_spark

1.1 process方法。

1.专门用来将存储在LinkedList对象下的message发送出去的一个方法。其实它处理这个message信息就是委托了它所持有的endpoint对象帮处理。
2.在这里传入这个dispatcher对象用到的时候仅是发送stop 信息后它需要将当前endpoint进行从其维护的列表里移除然后停止这么一个i行为。

/**
   * Process stored messages.
   */
  def process(dispatcher: Dispatcher): Unit = {
    var message: InboxMessage = null
    inbox.synchronized {
      if (!enableConcurrent && numActiveThreads != 0) {
        return
      }
      message = messages.poll()
      if (message != null) {
        numActiveThreads += 1
      } else {
        return
      }
    }
    while (true) {
      safelyCall(endpoint) {
        message match {
          case RpcMessage(_sender, content, context) =>
            try {
              endpoint.receiveAndReply(context).applyOrElse[Any, Unit](content, { msg =>
                throw new SparkException(s"Unsupported message $message from ${_sender}")
              })
            } catch {
              case NonFatal(e) =>
                context.sendFailure(e)
                // Throw the exception -- this exception will be caught by the safelyCall function.
                // The endpoint's onError function will be called.
                throw e
            }

          case OneWayMessage(_sender, content) =>
            endpoint.receive.applyOrElse[Any, Unit](content, { msg =>
              throw new SparkException(s"Unsupported message $message from ${_sender}")
            })

          case OnStart =>
            endpoint.onStart()
            if (!endpoint.isInstanceOf[ThreadSafeRpcEndpoint]) {
              inbox.synchronized {
                if (!stopped) {
                  enableConcurrent = true
                }
              }
            }

          case OnStop =>
            val activeThreads = inbox.synchronized { inbox.numActiveThreads }
            assert(activeThreads == 1,
              s"There should be only a single active thread but found $activeThreads threads.")
            dispatcher.removeRpcEndpointRef(endpoint)
            endpoint.onStop()
            assert(isEmpty, "OnStop should be the last message")

          case RemoteProcessConnected(remoteAddress) =>
            endpoint.onConnected(remoteAddress)

          case RemoteProcessDisconnected(remoteAddress) =>
            endpoint.onDisconnected(remoteAddress)

          case RemoteProcessConnectionError(cause, remoteAddress) =>
            endpoint.onNetworkError(cause, remoteAddress)
        }
      }

      inbox.synchronized {
        // "enableConcurrent" will be set to false after `onStop` is called, so we should check it
        // every time.
        if (!enableConcurrent && numActiveThreads != 1) {
          // If we are not the only one worker, exit
          numActiveThreads -= 1
          return
        }
        message = messages.poll()
        if (message == null) {
          numActiveThreads -= 1
          return
        }
      }
    }
  }

1.2 post方法。

这个方法就是用来存储待发送的信息,是线程安全的。

sparkdataset怎么分批处理_spark_02

二、Dispatcher类

这个类看名字就知道用来分发message的。

2.1 构造器与成员变量

1.其内部有一个内部类EndpointData,专门用来维护endpoint的名字与对应的inbox对象,如其名字就看成要发送的数据。
2.endpoints用来存储EndpointData对象,当向哪个endpoint发送数据的时候同过该endpoint名字查找即可找到。
3.receivers,一个存储即将需要发送出去的数据的队列。
4.threadpool ,一个线程池默认是操作系统CPU核数的2倍,其专门用来分发信息。

sparkdataset怎么分批处理_成员变量_03


sparkdataset怎么分批处理_数据_04

2.2 postMessage方法

直接通过endpoint名字从endpoints获取其要存储信息的Inbox,因为这个inbox跟EndpointData对象一一对应。

sparkdataset怎么分批处理_spark_05

2.3 MessageLoop方法

正如代码看到的就是当前线程不停地从blockingQueue里取数据出来,然后丢给对应的inbox进行处理。

sparkdataset怎么分批处理_sparkdataset怎么分批处理_06

三. outBox类

1.咱们看一下其构造器传参数,主要保存nettyEnv对象以及一个remote rpc地址这与inbox保存endpoint以及其引用不一样。nettyEnv用来创建连接的客户端或者是删除不需要维护的outbox。
2.看里面的成员变量,它持有TransportClient,一个netty的客户端专门用来与远程的endpoint节点进行通信的,这是inbox所没有的。

3.1主要成员变量

sparkdataset怎么分批处理_构造器_07

3.2主要的内部类

有两个内部类,都是用来发信息,主要关注sendWith方法,一个有回调函数,一个没有。

sparkdataset怎么分批处理_成员变量_08


sparkdataset怎么分批处理_sparkdataset怎么分批处理_09

3.2send方法

当调用的时候先将message添加进list中,然后开始将数据发送出去

sparkdataset怎么分批处理_成员变量_10

3.2drainOutbox方法

将list里面的信息都往远程节点发送直至排空,核心的两行代码就是从list里面获取message,然后委托message对象下的nettyClient将数据发送到nettyServer端帮处理该信息。

sparkdataset怎么分批处理_spark_11

四. CoarseGrainedExecutorBackend

1.再回到该类,上一篇在写cluster模式提交的时候咱们就看到了这个类的对象在最后被在一个container里启动并等待分发任务。但这次它是以一个endpoint对象的角色进行观察它在干啥。

2.既然是以一个endpoint的对象观察它,就看一下endpoint有哪些方法。它又是干啥的。从该接口注释看:一个为RPC专门定义的类,该类里的成员方法会因为不同类型的message会触发到,且endpoint的声明周期是[构造器->onStart->receive(多次)->onStop]上面看Inbox的时候也看到了,在实例inbox的时候就将一个OnStart 信息add进入了linkedList队列中。后面就调用了该endpoint的onstart方法了。

3.endpoint处了上面声明周期的方法外还有onDisconnected,onConnected等接口方法。

sparkdataset怎么分批处理_sparkdataset怎么分批处理_12

3.1 onStart()方法

endpoint接收到onStart信号后就开始后去driver的url,然后进行反向注册。

sparkdataset怎么分批处理_sparkdataset怎么分批处理_13

3.2 receive方法

一旦onstart且注册成功后就开始接收自己的或来自driver端的message类型,然后进行判断走对应的代码分支执行。

1.当下看到的 RegisteredExecutor和后面接收派发的任务的信息LaunchTask(data)类型。

sparkdataset怎么分批处理_数据_14

五.CoarseGrainedSchedulerBackend

就是这么一个粗粒度调度器专门分发任务给各个executor干活的,看一下它实现的接口ExecutorAllocationClient的注释,一个用于与集群通信的客户端目前仅支持yarn模式。

sparkdataset怎么分批处理_数据_15

5.1调度接口

与任务相关的操作方法。

sparkdataset怎么分批处理_sparkdataset怎么分批处理_16

5.2重要的成员变量

5.2.1executorDataMap

(1)下面用来维护各个executor的列表方便分发task给executor干活

sparkdataset怎么分批处理_成员变量_17

5.2.2 DriverEndpoint

driver端用于与其他executor通信的内部成员类,是它负责将task分发出去。接下来看一下重要的方法

sparkdataset怎么分批处理_数据_18

5.2.2.1 onStart方法

在启动后每隔一秒钟就调用endpointRef的send方法将一条message发送出去。

sparkdataset怎么分批处理_spark_19

5.2.2.2 recieve方法

根据接收到message类型匹配到后执行对应的分支代码。

sparkdataset怎么分批处理_spark_20

5.2.2.3 分发任务给executor

sparkdataset怎么分批处理_数据_21

5.2.2.4 launchTasks

(1)启动任务,这里也提到到了如果序列化的任务数据字节比特太大,大于默认的128x1024x1024字节就会建议开启广播变量进行序列化传输任务数据。或者说将默认的128这个值调大但不能超过Int.MaxValue/1024/1024否则就抛异常。

(2)一条重要的代码 executorData.executorEndpoint.send(LaunchTask(new SerializableBuffer(serializedTask)))

就是将序列化的任务包装成LaunchTask

sparkdataset怎么分批处理_构造器_22


sparkdataset怎么分批处理_成员变量_23

六 NettyRpcEndpointRef

上面已经看到了它要将LaunchTask类型的信息通过EndpointRef的send方法进行发送出去。我们就看一下这个send的方法

6.1send方法

它又将LaunchTask任务又进一步包装,进行适配enttyEnv的send这个方法,委托它将信息发送出去。

sparkdataset怎么分批处理_构造器_24

七 NettyRpcEnv

7.1重要的成员变量

分发器前面也见到了就一般就自己与自己通信,

transport*就是与远程executor通信关联的变量

sparkdataset怎么分批处理_数据_25

7.2 Send方法

正如6.1看到的它就委托当前这个方法将message发出去。

sparkdataset怎么分批处理_数据_26

7.2 postToOutbox方法

将message发送到executor,这里这个message对象就是上面OutboxMessage这个内部类,可以追回3.2的方法看它通过传入的客户端将信息发出去了。

sparkdataset怎么分批处理_成员变量_27

7.3 executor执行分发过来的task

回到类CoarseGrainedExecutorBackend。看receive方法,然后看关键代码行executor.launchTask这个方法。

sparkdataset怎么分批处理_成员变量_28


sparkdataset怎么分批处理_sparkdataset怎么分批处理_29

结尾

1.到此快速过完了一遍executor起来后等待driver调度分发任务,在这个分发任务的过程中,message信息被层层包装进行适配来自不同类的方法。
2.inbox主要处理本地的RPC,outbox主要处理remote RPC的信息。
3.当序列化的task字节大于128x1024x1024字节的时候,建议你要么使用广播变量要播继续调大这个值。