Spark 源码阅读分析(一)

在这里插入图片描述
这是 Spark 的基本框架图。

  1. Driver 程序初始化 SparkContext 对象;
  2. SparkContext 对象初始化过程中,连接集群资源管理器 Cluster Manager,申请资源,注册 app 信息;
  3. Cluster Manager 根据 Driver 申请的资源,在 worker 节点创建 Executor;
  4. 创建好的 Executor 将其信息发送到 Driver,申请注册;
  5. Driver 将创建一个 job,并交给 DAGSchedule;
  6. DAGSchedule 会把 job 分成多个 stages,并为每个 stage 创建一个 taskset 集合,集合中的 task 计算逻辑完全相同,只是处理的数据不同;
  7. DAGSchedule 会把 taskset 交给 TaskSchedule,TaskSchedule 再把 taskset 里的 task 发送给 Executor;
  8. Executor 接收到 task,会启动一个线程池 TaskRunner,在里面运行 task 任务。

这是一个 Application 应用程序执行的一系列基本操作。现在就通过分析源码的方式来追踪分析下代码是如何处理的。

SparkContext 对象负责与 Spark 集群交互,支持本地 local 模式和集群等模式。在开发 Spark 应用程序时,首先会创建 SparkConf 对象 – SparkConf 是参数配置的类;之后 SparkConf 对象作为参数传递给 SparkContext。

-- SparkContext.scala

class SparkContext(config: SparkConf) extends Logging {

  // The call site where this SparkContext was constructed.
  private val creationSite: CallSite = Utils.getCallSite()
 
  // 阻止同时有多个 SparkContext 实例被创建激活,因为一个JVM 只能有一个 SparkContext 实例。
  SparkContext.markPartiallyConstructed(this)

  val startTime = System.currentTimeMillis()

  private[spark] val stopped: AtomicBoolean = new AtomicBoolean(false)
  }

接着会对设置的 SparkConf 进行解析确认,判断所必须的参数是否缺失。这里可以发现,其通过 clone 浅拷贝一份副本,保证设置的参数不会被修改。

-- SparkContext.scala

try {
    _conf = config.clone()
    _conf.validateSettings()

    if (!_conf.contains("spark.master")) {
      throw new SparkException("A master URL must be set in your configuration")
    }
    if (!_conf.contains("spark.app.name")) {
      throw new SparkException("An application name must be set in your configuration")
    }

    _driverLogger = DriverLogger(_conf)

    // log out spark.app.name in the Spark driver logs
    logInfo(s"Submitted application: $appName")

完成上述参数的确认后,构建 Spark 的运行环境

-- SparkContext.scala

    // Create the Spark execution environment (cache, map output tracker, etc)
    _env = createSparkEnv(_conf, isLocal, listenerBus)
    SparkEnv.set(_env)

这里在上述程序执行过程中,SparkContext 初始化的一系列操作。


现在先来看看环境是如何创建的。

Spark 的环境处理在 SparkEnv.scala 中,这里可以知道,在初始化时,首先创建的是 Driver 的环境。

-- SparkEnv.scala

  private[spark] def createDriverEnv(
      conf: SparkConf,
      isLocal: Boolean,
      listenerBus: LiveListenerBus,
      numCores: Int,
      mockOutputCommitCoordinator: Option[OutputCommitCoordinator] = None): SparkEnv = {
    val ioEncryptionKey = if (conf.get(IO_ENCRYPTION_ENABLED)) {
      Some(CryptoStreamUtils.createKey(conf))
    } else {
      None
    }
    create(
      conf,
      SparkContext.DRIVER_IDENTIFIER,
      bindAddress,
      advertiseAddress,
      Option(port),
      isLocal,
      numCores,
      ioEncryptionKey,
      listenerBus = listenerBus,
      mockOutputCommitCoordinator = mockOutputCommitCoordinator
    )
  }

如果不考虑加密的话,或者说没有设置加密属性,那么走:

-- SparkEnv.scala

private def create(
      conf: SparkConf,
      executorId: String,
      bindAddress: String,
      advertiseAddress: String,
      port: Option[Int],
      isLocal: Boolean,
      numUsableCores: Int,
      ioEncryptionKey: Option[Array[Byte]],
      listenerBus: LiveListenerBus = null,
      mockOutputCommitCoordinator: Option[OutputCommitCoordinator] = None): SparkEnv = {

这里创建 Spark 环境,首先是 Driver 的环境。所以, executorId == SparkContext.DRIVER_IDENTIFIER

这里亦发现了,实例化了 Application 应用程序的类。

扫描二维码关注公众号,回复: 6114022 查看本文章
-- SparkEnv.scala

    def instantiateClass[T](className: String): T = {
      val cls = Utils.classForName(className)
      // Look for a constructor taking a SparkConf and a boolean isDriver, then one taking just
      // SparkConf, then one taking no arguments
      try {
        cls.getConstructor(classOf[SparkConf], java.lang.Boolean.TYPE)
          .newInstance(conf, java.lang.Boolean.valueOf(isDriver))
          .asInstanceOf[T]
      } catch {
        case _: NoSuchMethodException =>
          try {
            cls.getConstructor(classOf[SparkConf]).newInstance(conf).asInstanceOf[T]
          } catch {
            case _: NoSuchMethodException =>
              cls.getConstructor().newInstance().asInstanceOf[T]
          }
      }
    }

同时还实例化了:Boardcast,mapOutputTracker – 主要根据 map 任务把数据结果写到哪里。

同时也实例化了 shuffleManager,Shuffle 有两种管理器,分别是 sort 和 tungsten-sort。

-- SparkEnv.scala

val shortShuffleMgrNames = Map(
      "sort" -> classOf[org.apache.spark.shuffle.sort.SortShuffleManager].getName,
      "tungsten-sort" -> classOf[org.apache.spark.shuffle.sort.SortShuffleManager].getName)
    val shuffleMgrName = conf.get(config.SHUFFLE_MANAGER)
    val shuffleMgrClass =
      shortShuffleMgrNames.getOrElse(shuffleMgrName.toLowerCase(Locale.ROOT), shuffleMgrName)
    val shuffleManager = instantiateClass[ShuffleManager](shuffleMgrClass)

实例化了内存管理器

-- SparkEnv.scala

val memoryManager: MemoryManager = UnifiedMemoryManager(conf, numUsableCores)

以及块管理器。BlockManagerMaster 块管理器是用来获取、协调、管理远程节点上的 Block 集合。创建的 BlockManager,主要运行在 Worker 节点上。这里虽然创建成功了,但是只有在 BlockManager 中初始化函数之后才执行相关操作。

-- SparkEnv.scala

    val blockManagerPort = if (isDriver) {
      conf.get(DRIVER_BLOCK_MANAGER_PORT)
    } else {
      conf.get(BLOCK_MANAGER_PORT)
    }

    val blockTransferService =
      new NettyBlockTransferService(conf, securityManager, bindAddress, advertiseAddress,
        blockManagerPort, numUsableCores)

    val blockManagerMaster = new BlockManagerMaster(registerOrLookupEndpoint(
      BlockManagerMaster.DRIVER_ENDPOINT_NAME,
      new BlockManagerMasterEndpoint(rpcEnv, isLocal, conf, listenerBus)),
      conf, isDriver)

至此,Spark 运行环境基本在 SparkEnv.create中初始化完毕。


猜你喜欢

转载自blog.csdn.net/dec_sun/article/details/89470298