spark源码解析

如何阅读源码?
任何一个程序,或者一个框架,无论做什么,多么复杂,都会有唯一的入口。通过这个入口,能够找到一条主线,这条主线就是这个程序或者框架的核心。
围绕这条主线,追溯整个调用链路,就能发掘出框架中的核心抽象,将这些抽象的作用搞懂,同时将它们之间的关系通过uml表示出来,这样,源码的大体结构就一览无余了。
源码会涉及很多包,其实包本身就是对源码的分类和抽象,这些包的功能也要弄清楚。源码分为核心代码和辅助代码,比如io相关,属于辅助部分。core包下面一般都是主流程下核心代码。
理清主线,理清抽象概念,理清抽象之间的关系之后,再去看具体的方法和处理过程,相信,这样阅读起来就顺畅的多。
还有一些比较难理解的点,比如处理某些特定问题时的思路和算法,或者是某种复杂的数据结构,需要着重挖掘。
源码都是水平较高的程序员所写,使用面向对象进行抽象,还会涉及到很多设计模式,所以读源码之前,对各种设计模式要有所了解。
另外最重要的基础,其实是语言基础。比如阅读spark源码,scala基础一定要好,这样在阅读的过程中才不会被各种拦路虎拦住。

寻找入口,有两种方式。
一是找到框架或者程序的二进制程序,然后追溯编译该程序的类。
二是启动之后阅读日志,从日志中追溯主调用链。

以上。

以spark为例子。
通过spark-submit命令提交spark任务,能够找到对应的启动类org.apache.spark.deploy.SparkSubmit,其main方法如下

  def main(args: Array[String]): Unit = {
    val appArgs = new SparkSubmitArguments(args)
    if (appArgs.verbose) {
      // scalastyle:off println
      printStream.println(appArgs)
      // scalastyle:on println
    }
    appArgs.action match {
      case SparkSubmitAction.SUBMIT => submit(appArgs)
      case SparkSubmitAction.KILL => kill(appArgs)
      case SparkSubmitAction.REQUEST_STATUS => requestStatus(appArgs)
    }
  }

submit方法分成两步,首先准备环境,设置类加载路径(classpath),环境变量,启动类,其次通过反射调用用户的spark任务

val mainMethod = mainClass.getMethod("main", new Array[String](0).getClass)
try {
      mainMethod.invoke(null, childArgs.toArray)
    } catch {
      case t: Throwable =>
        findCause(t) match {
          case SparkUserAppException(exitCode) =>
            System.exit(exitCode)

          case t: Throwable =>
            throw t
        }
    }

用户的任务中首先需要创建sparkSession,创建时候先查看当前线程中是否已有实例

  /** The active SparkSession for the current thread. */
  private val activeThreadSession = new InheritableThreadLocal[SparkSession]
def getOrCreate(): SparkSession = synchronized {
        // Get the session from current thread's active session.
      var session = activeThreadSession.get()
      if ((session ne null) && !session.sparkContext.isStopped) {
        options.foreach { case (k, v) => session.conf.set(k, v) }
        if (options.nonEmpty) {
          logWarning("Using an existing SparkSession; some configuration may not take effect.")
        }
        return session
      }
}

如果没有,使用默认defaultSession(从哪里来?),如果defaultSession不可用,创建。创建sparkSession前,首先创建sparkContext

// No active nor global default session. Create a new one.
val sparkContext = userSuppliedContext.getOrElse {
  // set app name if not given
  val randomAppName = java.util.UUID.randomUUID().toString
  val sparkConf = new SparkConf()
  options.foreach { case (k, v) => sparkConf.set(k, v) }
  if (!sparkConf.contains("spark.app.name")) {
    sparkConf.setAppName(randomAppName)
  }
  val sc = SparkContext.getOrCreate(sparkConf)
  // maybe this is an existing SparkContext, update its SparkConf which maybe used
  // by SparkSession
  options.foreach { case (k, v) => sc.conf.set(k, v) }
  if (!sc.conf.contains("spark.app.name")) {
    sc.conf.setAppName(randomAppName)
  }
  sc
}

所以关键类是sparkContext,嗯,最核心的类了,先看看它有哪些核心成员变量。

private var _conf: SparkConf = _
private var _eventLogDir: Option[URI] = None
private var _eventLogCodec: Option[String] = None
private var _env: SparkEnv = _  //提供spark任务运行时环境
private var _jobProgressListener: JobProgressListener = _  // 跟踪task级的各种信息,用于展示在spark ui
private var _statusTracker: SparkStatusTracker = _  // 提供监控job和stage的api,有些被应用到spark ui
private var _progressBar: Option[ConsoleProgressBar] = None  // 用于在控制台输出stage进程信息
private var _ui: Option[SparkUI] = None
private var _hadoopConfiguration: Configuration = _
private var _executorMemory: Int = _
private var _schedulerBackend: SchedulerBackend = _  // 后台调度进程
private var _taskScheduler: TaskScheduler = _  // 通过SchedulerBackend实现调度的task调度器
private var _heartbeatReceiver: RpcEndpointRef = _
@volatile private var _dagScheduler: DAGScheduler = _  // 面向stage的调度器,核心类,切分stage,构成DAG都在此类中完成
private var _applicationId: String = _
private var _applicationAttemptId: Option[String] = None
private var _eventLogger: Option[EventLoggingListener] = None
private var _executorAllocationManager: Option[ExecutorAllocationManager] = None  // 根据负载动态分配或者回收executor
private var _cleaner: Option[ContextCleaner] = None  // 异步清理器,清除RDD,shuffle和broadcast的状态
private var _listenerBusStarted: Boolean = false
private var _jars: Seq[String] = _
private var _files: Seq[String] = _
private var _shutdownHookRef: AnyRef = _

逐个过一下核心的成员变量。
sparkEnv提供spark任务的运行时环境,看一类的声明

  class SparkEnv (
    val executorId: String,
    private[spark] val rpcEnv: RpcEnv,
    val serializer: Serializer,
    val closureSerializer: Serializer,
    val serializerManager: SerializerManager,
    val mapOutputTracker: MapOutputTracker,
    val shuffleManager: ShuffleManager,
    val broadcastManager: BroadcastManager,
    val blockManager: BlockManager,
    val securityManager: SecurityManager,
    val metricsSystem: MetricsSystem,
    val memoryManager: MemoryManager,
    val outputCommitCoordinator: OutputCommitCoordinator,
    val conf: SparkConf
)

(未完待续)

你可能感兴趣的:(spark源码解析)