【Spark】SparkContext源碼解讀

來源:互聯網
上載者:User

標籤:

SparkContext的初始化

SparkContext是應用啟動時建立的Spark內容物件,是進行Spark應用開發的主要介面,是Spark上層應用與底層實現的中轉站(SparkContext負責給executors發送task)。
SparkContext在初始化過程中,主要涉及一下內容:

  • SparkEnv
  • DAGScheduler
  • TaskScheduler
  • SchedulerBackend
  • SparkUI
產生SparkConf

SparkContext的建構函式中最重要的入參是SparkConf。SparkContext進行初始化的時候,首先要根據初始化入參來構建SparkConf對象,進而再去建立SparkEnv。

建立SparkConf對象來管理spark應用的屬性設定。SparkConf類比較簡單,是通過一個HashMap容器來管理key、value類型的屬性。
為SparkConf類聲明,其中setting變數為HashMap容器:

下面是SparkContext類中,關於SparkConf對象的拷貝過程:

建立LiveListenerBus監聽器

這是典型的觀察者模式,向LiveListenerBus類註冊不同類型的SparkListenerEvent事件,SparkListenerBus會遍曆它的所有監聽者SparkListener,然後找出事件對應的介面進行響應。

下面是SparkContext建立LiveListenerBus對象:

  // An asynchronous listener bus for Spark events  private[spark] val listenerBus = new LiveListenerBus
建立SparkEnv運行環境

在SparkEnv中建立了MapOutputTracker、MasterActor、BlockManager、CacheManager、HttpFileServer一系列對象。
為產生SparkEnv的代碼:

SparkEnv的建構函式入參列表為:

class SparkEnv (    val executorId: String,    val actorSystem: ActorSystem,    val serializer: Serializer,    val closureSerializer: Serializer,    val cacheManager: CacheManager,    val mapOutputTracker: MapOutputTracker,    val shuffleManager: ShuffleManager,    val broadcastManager: BroadcastManager,    val blockTransferService: BlockTransferService,    val blockManager: BlockManager,    val securityManager: SecurityManager,    val httpFileServer: HttpFileServer,    val sparkFilesDir: String,    val metricsSystem: MetricsSystem,    val shuffleMemoryManager: ShuffleMemoryManager,    val outputCommitCoordinator: OutputCommitCoordinator,    val conf: SparkConf) extends Logging

這裡說明幾個入參的作用:

  • cacheManager: 用於儲存中間計算結果
  • mapOutputTracker: 用來緩衝MapStatus資訊,並提供從MapOutputMaster擷取資訊的功能
  • shuffleManager: 路由維護表
  • broadcastManager: 廣播
  • blockManager: 塊管理
  • securityManager: 安全管理
  • httpFileServer: 檔案儲存體伺服器
    *l sparkFilesDir: 檔案儲存體目錄
  • metricsSystem: 測量
  • conf: 設定檔
建立SparkUI

下面是SparkContext初始化SparkUI的代碼:

其中,在SparkUI對象初始化函數中,註冊了StorageStatusListener監聽器,負責監聽Storage的變化及時的展示到Spark web頁面上。attachTab方法中添加對象正是我們在Spark Web頁面中看到的那個標籤。

  /** Initialize all components of the server. */  def initialize() {    attachTab(new JobsTab(this))    val stagesTab = new StagesTab(this)    attachTab(stagesTab)    attachTab(new StorageTab(this))    attachTab(new EnvironmentTab(this))    attachTab(new ExecutorsTab(this))    attachHandler(createStaticHandler(SparkUI.STATIC_RESOURCE_DIR, "/static"))    attachHandler(createRedirectHandler("/", "/jobs", basePath = basePath))    attachHandler(      createRedirectHandler("/stages/stage/kill", "/stages", stagesTab.handleKillRequest))  }
建立TaskScheduler和DAGScheduler並啟動運行

在SparkContext中, 最主要的初始化工作就是建立TaskScheduler和DAGScheduler, 這兩個就是Spark的核心所在。

Spark的設計非常的乾淨, 把整個DAG抽象層從實際的task執行中剝離了出來DAGScheduler, 負責解析spark命令,產生stage, 形成DAG, 最終劃分成tasks, 提交給TaskScheduler, 他只完成靜態分析TaskScheduler,專門負責task執行, 他只負責資源管理, task分配, 執行情況的報告。
這樣設計的好處, 就是Spark可以通過提供不同的TaskScheduler簡單的支援各種資源調度和執行平台

下面代碼是根據Spark的運行模式來選擇相應的SchedulerBackend,同時啟動TaskScheduler:

其中,createTaskScheduler最為關鍵的一點就是根據master變數來判斷Spark當前的部署方式,進而產生相應的SchedulerBackend的不同子類。建立的SchedulerBackend放置在TaskScheduler中,在後續的Task分發過程中扮演著重要角色。

TaskScheduler.start的目的是啟動相應的SchedulerBackend,並啟動定時器進行檢測,下面是該函數源碼(定義在TaskSchedulerImpl.scala檔案中):

  override def start() {    backend.start()    if (!isLocal && conf.getBoolean("spark.speculation", false)) {      logInfo("Starting speculative execution thread")      import sc.env.actorSystem.dispatcher      sc.env.actorSystem.scheduler.schedule(SPECULATION_INTERVAL milliseconds,            SPECULATION_INTERVAL milliseconds) {        Utils.tryOrExit { checkSpeculatableTasks() }      }    }  }
添加EventLoggingListener監聽器

這個預設是關閉的,可以通過spark.eventLog.enabled配置開啟。它主要功能是以json格式記錄發生的事件:

  // Optionally log Spark events  private[spark] val eventLogger: Option[EventLoggingListener] = {    if (isEventLogEnabled) {      val logger =        new EventLoggingListener(applicationId, eventLogDir.get, conf, hadoopConfiguration)      logger.start()      listenerBus.addListener(logger)      Some(logger)    } else None  }
加入SparkListenerEvent事件

往LiveListenerBus中加入了SparkListenerEnvironmentUpdate、SparkListenerApplicationStart兩類事件,對這兩種事件監聽的監聽器就會調用onEnvironmentUpdate、onApplicationStart方法進行處理。

  setupAndStartListenerBus()  postEnvironmentUpdate()  postApplicationStart()
SparkContext類中的關鍵函數textFile

要載入被處理的資料, 最常用的textFile, 其實就是產生HadoopRDD, 作為起始的RDD

  /**   * Read a text file from HDFS, a local file system (available on all nodes), or any   * Hadoop-supported file system URI, and return it as an RDD of Strings.   */  def textFile(path: String, minPartitions: Int = defaultMinPartitions): RDD[String] = {    assertNotStopped()    hadoopFile(path, classOf[TextInputFormat], classOf[LongWritable], classOf[Text],      minPartitions).map(pair => pair._2.toString).setName(path)  }    /** Get an RDD for a Hadoop file with an arbitrary InputFormat   *   * ‘‘‘Note:‘‘‘ Because Hadoop‘s RecordReader class re-uses the same Writable object for each   * record, directly caching the returned RDD or directly passing it to an aggregation or shuffle   * operation will create many references to the same object.   * If you plan to directly cache, sort, or aggregate Hadoop writable objects, you should first   * copy them using a `map` function.   */  def hadoopFile[K, V](      path: String,      inputFormatClass: Class[_ <: InputFormat[K, V]],      keyClass: Class[K],      valueClass: Class[V],      minPartitions: Int = defaultMinPartitions      ): RDD[(K, V)] = {    assertNotStopped()    // A Hadoop configuration can be about 10 KB, which is pretty big, so broadcast it.    val confBroadcast = broadcast(new SerializableWritable(hadoopConfiguration))    val setInputPathsFunc = (jobConf: JobConf) => FileInputFormat.setInputPaths(jobConf, path)    new HadoopRDD(      this,      confBroadcast,      Some(setInputPathsFunc),      inputFormatClass,      keyClass,      valueClass,      minPartitions).setName(path)  }
runJob

關鍵在於調用了dagScheduler.runJob

  /**   * Run a function on a given set of partitions in an RDD and pass the results to the given   * handler function. This is the main entry point for all actions in Spark. The allowLocal   * flag specifies whether the scheduler can run the computation on the driver rather than   * shipping it out to the cluster, for short actions like first().   */  def runJob[T, U: ClassTag](      rdd: RDD[T],      func: (TaskContext, Iterator[T]) => U,      partitions: Seq[Int],      allowLocal: Boolean,      resultHandler: (Int, U) => Unit) {    if (stopped) {      throw new IllegalStateException("SparkContext has been shutdown")    }    val callSite = getCallSite    val cleanedFunc = clean(func)    logInfo("Starting job: " + callSite.shortForm)    if (conf.getBoolean("spark.logLineage", false)) {      logInfo("RDD‘s recursive dependencies:\n" + rdd.toDebugString)    }    dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, allowLocal,      resultHandler, localProperties.get)    progressBar.foreach(_.finishAll())    rdd.doCheckpoint()  }
說明

以上的源碼解讀基於spark-1.3.1原始碼工程檔案

轉載請註明作者Jason Ding及其出處
GitCafe部落客頁(http://jasonding1354.gitcafe.io/)
Github部落客頁(http://jasonding1354.github.io/)
CSDN部落格(http://blog.csdn.net/jasonding1354)
簡書首頁(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)
Google搜尋jasonding1354進入我的部落客頁

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

【Spark】SparkContext源碼解讀

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.