Spark1.6-----源码解读之SparkEnv

我是跟着源码点进去一步一写的,所以观看时希望大家能一步一跟着源码走,不要只看博文。

在SparkContext 284行创建SparkEnv:

  // This function allows components created by SparkEnv to be mocked in unit tests:
  private[spark] def createSparkEnv(
      conf: SparkConf,
      isLocal: Boolean,
      listenerBus: LiveListenerBus): SparkEnv = {
    SparkEnv.createDriverEnv(conf, isLocal, listenerBus, SparkContext.numDriverCores(master))
  }

SparkEnv.createDriverEnv最终会调用SparkEnv 233行create:

  /**
   * Helper method to create a SparkEnv for a driver or an executor.
   */
  private def create(
      conf: SparkConf,
      executorId: String,
      hostname: String,
      port: Int,
      isDriver: Boolean,
      isLocal: Boolean,
      numUsableCores: Int,
      listenerBus: LiveListenerBus = null,
      mockOutputCommitCoordinator: Option[OutputCommitCoordinator] = None): SparkEnv = {

该方法会构造如下几个重要的成员:

    ①分布式消息系统RpcEnv

    ②mapOutputTracker

    ③ShuffleManager

    ④BlockManager

一一解答:

2,RpcEnv构建

个人认为,如果把分布式系统(HDFS, HBASE,SPARK等)比作一个人,那么RPC可以认为是人体的血液循环系统。它将系统中各个不同的组件(如Hbase中的master, Regionserver, client)联系了起来。同样,在spark中,不同组件像driver,executor,worker,master(stanalone模式)之间的通信也是基于RPC来实现的。

Spark 1.6之前,spark的RPC是基于Akaa来实现的。Akka是一个基于scala语言的异步的消息框架。Spark1.6后,spark借鉴Akka的设计自己实现了一个基于Netty的rpc框架。大概的原因是1.6之前,RPC通过Akka来实现,而大文件是基于netty来实现的,加之akka版本兼容性问题,所以1.6之后把Akka改掉了,具体jira见(https://issues.apache.org/jira/browse/SPARK-5293)。

本文主要对spark1.6之后基于netty新开发的rpc框架做一个较为深入的分析。

2.1整体架构

spark 基于netty新的rpc框架借鉴了Akka的中的设计,它是基于Actor模型,各个组件可以认为是一个个独立的实体,各个实体之间通过消息来进行通信。具体各个组件之间的关系图如下(图片来自[1]):

2.2 RpcEndpoint

表示一个个需要通信的个体(如master,worker,driver),主要根据接收的消息来进行对应的处理。一个RpcEndpoint经历的过程依次是:构建->onStart→receive→onStop。其中onStart在接收任务消息前调用,receive和receiveAndReply分别用来接收另一个RpcEndpoint(也可以是本身)send和ask过来的消息。

2.3 RpcEndpointRef

RpcEndpointRef是对远程RpcEndpoint的一个引用。当我们需要向一个具体的RpcEndpoint发送消息时,一般我们需要获取到该RpcEndpoint的引用,然后通过该应用发送消息。

2.4 RpcAddress

表示远程的RpcEndpointRef的地址,Host + Port。

2.5 RpcEnv

RpcEnv为RpcEndpoint提供处理消息的环境。RpcEnv负责RpcEndpoint整个生命周期的管理,包括:注册endpoint,endpoint之间消息的路由,以及停止endpoint。

SparkEnv 253行create创建RpcEnv:

    val rpcEnv = RpcEnv.create(actorSystemName, hostname, port, conf, securityManager,
      clientMode = !isDriver)

调用RpcEnv 53行getRpcEnvFactory:

    getRpcEnvFactory(conf).create(config)

 解读:同过RpcEnvFactory创建RpcEnv

RpcEnv 35行getRpcEnvFactory具体实现:

  private def getRpcEnvFactory(conf: SparkConf): RpcEnvFactory = {
    val rpcEnvNames = Map(
      "akka" -> "org.apache.spark.rpc.akka.AkkaRpcEnvFactory",
      "netty" -> "org.apache.spark.rpc.netty.NettyRpcEnvFactory")
    val rpcEnvName = conf.get("spark.rpc", "netty")
    val rpcEnvFactoryClassName = rpcEnvNames.getOrElse(rpcEnvName.toLowerCase, rpcEnvName)
    Utils.classForName(rpcEnvFactoryClassName).newInstance().asInstanceOf[RpcEnvFactory]
  }

解读:根据配置创建对应的rpc框架 ,默认为netty,通过反射创建RpcEnvFactory。

3, ActorySystem

SparkEnv 265行:

        // Create a ActorSystem for legacy codes
        AkkaUtils.createActorSystem(
          actorSystemName + "ActorSystem",
          hostname,
          actorSystemPort,
          conf,
          securityManager
        )._1

AkkaUtils 53行:

doCreateActorSystem(name, host, actualPort, conf, securityManager)

最终在AkkaUtiles 121行创建actorSystem:

   val actorSystem = ActorSystem(name, akkaConf)

解析 :因为不是很重要就没有解读了。

4, mapOutputTracker创建

mapoutputTracker用于跟踪map的输出状态,便于reduce获取。每个map或者reduce任务都会有唯一任务id。每个reduce任务的输入可能有多个map输出,reduce会到各个map任务的节点拉取Block这个过程叫shuffle,每个shuffle过程都有一个唯一标识shuffleId。

SparkEnv  328行创建mapOutputTracker:

    //根据isDriver创建不同的对象。
    val mapOutputTracker = if (isDriver) {
      new MapOutputTrackerMaster(conf)
    } else {
      new MapOutputTrackerWorker(conf)
    }

先看MapOutputTrackerMaster。

MapOutputTrackerMaster 273行 构造器:

private[spark] class MapOutputTrackerMaster(conf: SparkConf)
  extends MapOutputTracker(conf) {

比较重要的成员对象 MapOutputTracker 299行:mapStatuses,cachedSerializedStatuses 

//key为shuffleId,Array中的MapStatus跟踪各个map任务的输出状态,Mapstatus中维护的了map输出
//block的地址BolckMangerId,所以reduce知道从何处获得map任务的输出状态。
protected val mapStatuses = new TimeStampedHashMap[Int, Array[MapStatus]]()
//维护了序列化后map任务的输出状态。
private val cachedSerializedStatuses = new TimeStampedHashMap[Int, Array[Byte]]()

MapOutputTrackerWorker:

/**
 * MapOutputTracker for the executors, which fetches map output information from the driver's
 * MapOutputTrackerMaster.
 */
//从driver端获取对应的map output信息
private[spark] class MapOutputTrackerWorker(conf: SparkConf) extends MapOutputTracker(conf) {
  protected val mapStatuses: Map[Int, Array[MapStatus]] =
    new ConcurrentHashMap[Int, Array[MapStatus]]().asScala
}

回到SparkEnv

SparkEnv 334行构建MapOutputTrackerMasterEndpoint并获取MapOutputTrackerMasterEndpoint引用:

    // Have to assign trackerActor after initialization as MapOutputTrackerActor
    // requires the MapOutputTracker itself
    mapOutputTracker.trackerEndpoint = registerOrLookupEndpoint(MapOutputTracker.ENDPOINT_NAME,
      new MapOutputTrackerMasterEndpoint(
        rpcEnv, mapOutputTracker.asInstanceOf[MapOutputTrackerMaster], conf))

SparkEnv 317行registerOrLookupEndpoint具体实现:

    def registerOrLookupEndpoint(
        name: String, endpointCreator: => RpcEndpoint):
      RpcEndpointRef = {
      if (isDriver) {
        logInfo("Registering " + name)
        rpcEnv.setupEndpoint(name, endpointCreator)
      } else {
        RpcUtils.makeDriverRef(name, conf, rpcEnv)
      }
    }

解析:如果是Driver会创建MapOutputTrackerMasterEndpoint 然后获得MapOutputTrackerMasterEndpoint的引用RpcEndpointRef,

Executor直接获得MapOutputTrackerMasterEndpoint的引用RpcEndpointRef。

RpcEndpointRef可以理解为对象的引用。

map任务的状态是由Executor是向MapOutputTrackerMasterEndpoint发送消息来将map状态同步到MapStatus中的。registerOrLookupEndpoint获得的引用就是找到MapOutputTrackerMasterEndpoint的关键。

5, ShuffleManager

SparkEnv 341:

    val shortShuffleMgrNames = Map(
      "hash" -> "org.apache.spark.shuffle.hash.HashShuffleManager",
      "sort" -> "org.apache.spark.shuffle.sort.SortShuffleManager",
      "tungsten-sort" -> "org.apache.spark.shuffle.sort.SortShuffleManager")
    val shuffleMgrName = conf.get("spark.shuffle.manager", "sort")
    val shuffleMgrClass = shortShuffleMgrNames.getOrElse(shuffleMgrName.toLowerCase, shuffleMgrName)
    val shuffleManager = instantiateClass[ShuffleManager](shuffleMgrClass)

解读:ShuffleManager负责管理本地和远程的shuffle操作的。使用反射的方式生成的(默认)SortShuffleManager。

6  BlockManger

SparkEnv 364:

    // NB: blockManager is not valid until initialize() is called later.
    val blockManager = new BlockManager(executorId, rpcEnv, blockManagerMaster,
      serializer, conf, memoryManager, mapOutputTracker, shuffleManager,
      blockTransferService, securityManager, numUsableCores)

解读:BlockManager负责对Block管理。

总结

创建了几个很重要的成员。

用于进行消息传递的RpcEnv

用于负责跟踪map输出的mapOutputTracker

另外两个以后的博客会讲

猜你喜欢

转载自blog.csdn.net/qq_33872191/article/details/85016502
今日推荐