標籤:spark runtime(driver、masster、worker、executor)內幕解密
內容:
1、再論Spark叢集部署;
2、Job提交解密;
3、Job的產生和接受;
4、Task的運行;
5、再論Shuffle;
從一個作業視角,透過Master、Drvier、Executor來透視Spark Runtime
==========再論Spark叢集部署============
官網中關於叢集的部署:
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
預設情況下,每個Worker下有一個Executor,會最大化的使用記憶體和CPU。
Master髮指令給Worker來分配資源,不關心Worker能不能分配到這個資源,他發給多少資源就是多少資源。
1、從Spark Runtime的角度講,由五大核心對象:Master、Worker、Executor、Drvier、CoarseGrainedExecutorBackend;
2、Spark在做分布式叢集系統設計的時候,最大化功能獨立、模組化封裝具體獨立的對象、強內聚松耦合;
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
3、當Drvier中的SparkContext初始化的時候會提交程式給Master,Master如果接受該程式在Spark中啟動並執行話,就會為當前的程式分配AppID,同時會分配具體的計算資源,需要特別注意的是,Master是根據當前提交程式的配置資訊來給叢集中的Worker髮指令分配具體的計算資源,但是,Master發出指令後並不關心具體的資源是否已經分配,轉過來說Master是髮指令後就記錄了分配的資源,以後用戶端再次提交其它的程式程式的話,就不能夠使用該資源了,
弊端:可能導致其它要提交的程式無法提交到本來要分配到的計算資源,沒法運行;最重要的優勢:在Spark分布式系統功能在弱耦合的基礎上最快的運行系統(否則如果Master要等到資源最終分配成功後才通知Driver的話,就會造成Driver阻塞,不能夠最大化並行計算資源的使用率);
因為Spark預設情況下,程式是排隊執行的。
需要補充說明的是,Spark在預設情況下由於叢集中一般都只有一個Application在運行,所以Master分配資源策略的弊端就沒有那麼明顯了。
==========Job提交過程源碼解密============
先運行一個程式,看日誌
1、一個非常重要的技巧,通過在spark-shell中運行一個Job來瞭解Job提交的過程,然後再用源碼驗證這個過程;
scala> sc.textFile("/library/dataforSortedShuffle").flatMap(_.split(" ")).map(word=>(word,1)).reduceByKey(_+_).saveAsTextFile("/library/dataoutput2")
日誌資訊:
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
2、Spark中所有的Action都會至少一個Job
上面日誌顯示,在saveAsTextFile的時候,會觸發一個Job。
3、SparkContext在執行個體化的時候會構造SparkDeploySchedulerBackend、DAGScheduler、TaskSchedulerImpl、MapOutputTrackerMaster等對象,其中SparkDeploySchedulerBackend負責叢集計算資源的管理和調度,DAGScheduler負責高層調度(例如Job中Stage劃分、資料本地性等內容),TaskSchedulerImpl負責具體Stage內部的底層調度(例如:具體每個Task的調度、Task的容錯等),MapOutputTrackerMaster負責Shuffle中資料輸出和讀取的管理;
4、TaskSchedulerImpl內部的調度(就是整段日誌的一部分):
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
==========Task的運行解密============
1、Task是運行在Executor中,而Executor又是位於CoarseGrainedExecutorBackend中的,且CoarseGrainedExecutorBackend和Executor是一一對應的;
這裡進程就有:
650) this.width=650;" src="/e/u261/themes/default/images/spacer.gif" style="background:url("/e/u261/lang/zh-cn/images/localimage.png") no-repeat center;border:1px solid #ddd;" alt="spacer.gif" />
2、當CoarseGrainedExecutorBackend接收到TaskSetManager發過來的LaunchTask訊息後,會還原序列化Task Description,然後使用CoarseGrainedExecutorBackend中唯一的executor來執行任務
override def receive: PartialFunction[Any, Unit] = {
case RegisteredExecutor(hostname) =>
logInfo("Successfully registered with driver")
executor = new Executor(executorId, hostname, env, userClassPath, isLocal = false)
case RegisterExecutorFailed(message) =>
logError("Slave registration failed: " + message)
System.exit(1)
case LaunchTask(data) =>
if (executor == null) {
logError("Received LaunchTask command but executor was null")
System.exit(1)
} else {
val taskDesc = ser.deserialize[TaskDescription](data.value)
logInfo("Got assigned task " + taskDesc.taskId)
executor.launchTask(this, taskId = taskDesc.taskId, attemptNumber = taskDesc.attemptNumber,
taskDesc.name, taskDesc.serializedTask)
}
叢集:
private[spark] object CoarseGrainedClusterMessages {
case object RetrieveSparkProps extends CoarseGrainedClusterMessage
// Driver to executors
case class LaunchTask(data: SerializableBuffer) extends CoarseGrainedClusterMessage
case class KillTask(taskId: Long, executor: String, interruptThread: Boolean)
extends CoarseGrainedClusterMessage
sealed trait RegisterExecutorResponse
case class RegisteredExecutor(hostname: String) extends CoarseGrainedClusterMessage
with RegisterExecutorResponse
case class RegisterExecutorFailed(message: String) extends CoarseGrainedClusterMessage
with RegisterExecutorResponse
補充說明:LaunchTask是case class
executor中看到
// Start worker thread pool
private val threadPool = ThreadUtils.newDaemonCachedThreadPool("Executor task launch worker")
private val executorSource = new ExecutorSource(threadPool, executorId)
線程池啊!!!
作業:
Job提交和執行的過程總結一下。
王家林老師名片:
中國Spark第一人
新浪微博:http://weibo.com/ilovepains
公眾號:DT_Spark
部落格:http://blog.sina.com.cn/ilovepains
手機:18610086859
QQ:1740415547
郵箱:[email protected]
本文出自 “一枝花傲寒” 部落格,謝絕轉載!
Spark Runtime(Driver、Masster、Worker、Executor)內幕解密(DT大資料夢工廠)