寫在前面的話:本篇博客爲原創,認真閱讀需要比對spark 2.1.1的源碼,預計閱讀耗時30分鐘,如果大家發現有問題或者是不懂的,歡迎討論
歡迎關注公衆號:後來X
spark 2.1.1的源碼包(有需要自取):關注公衆號【後來X】,回覆spark源碼
第一次寫博客,寫的有啥問題,歡迎大家留言評論,一定每週更新,哈哈!
今天主要分析的是spark的YarnCluster模式下的提交任務的源碼,那麼我們先看一下流程圖
開始啃源碼吧,爲了啃源碼更高效,希望大家把這張流程圖搭配着一起看,可以時刻知道現在到哪一步了。
正式開始源碼分析
Spark-submit命令
說到提交任務,不管是什麼spark的哪種運行模式,提交任務的命令都少不了Spark-submit,下面以提交wordCount的項目的命令爲例:
bin/spark-submit \
--class com.later.WordCount \
--master yarn \
--deploy-mode cluster \
./test/jars/spark-WordCount.jar \
10
那我們提交完之後,都會有一個spark-submit的線程,所以在spark源碼中,double shift我們先找到SparkSubmit.scala
-
先找到main方法,並且提交參數的 .action 默認爲submit,所以匹配到提交作業的命令:submit(appArgs)
-
準備提交job的環境:prepareSubmitEnvironment(args)
在這個裏面,主要是對參數的賦值,我們額外注意childMainClass,通過ctrl+F搜索,發現:
cluster模式->childMainClass = “org.apache.spark.deploy.yarn.Client”
client模式->childMainClass = “com.later.WordCount” -
最後doRunMain(),點進去
-
判斷代理是否爲null,無論是if還是else,都執行
runMain(childArgs, childClasspath, sysProps, childMainClass, args.verbose)
-
在這裏發現:定義了一個mainClass對象,並把上面第2步拿到的childMainClass賦給了mainClass
-
並且往下滑,發現還通過反射的方式獲得mainClass中的main方法,賦給了mainMethod
-
將mainClass中的main方法當做自己的方法調用(回調)
此時的mainClass對象爲:org.apache.spark.deploy.yarn.Client
yarnClient的啓動
因爲在上一次submit最終回調的是這個Client裏面的main方法,所以double shift找到org.apache.spark.deploy.yarn.Client
-
找到main方法,並且創建了Client,執行了run方法
-
向ResourceManager提交申請,獲取appID,點進去
-
既然提出申請,那就需要
**創建申請: ** val newApp = yarnClient.createApplication()
創建該申請的響應: val newAppResponse = newApp.getNewApplicationResponse()
設置啓動AM的環境:
val appContext = createApplicationSubmissionContext(newApp, containerContext)
-
這是設置適當的上下文來啓動AM,也就是封裝命令的具體內容 :
val containerContext = createContainerLaunchContext(newAppResponse)
那麼既然要啓動AM,就需要有相應的參數,在該方法中點進去,可以看到:javaOpts+=內存,GC,日誌
並且獲取到了val userClass = 我們在提交任務的時候,–class 之後提交的參數,例如“com.later.wordCount”,並且還拿到了amClass
cluster->val amClass = “org.apache.spark.deploy.yarn.ApplicationMaster”
client->amClass=“org.apache.spark.deploy.yarn.ExecutorLauncher”
AM的命令:val commands =
/bin/java “org.apache.spark.deploy.yarn.ApplicationMaster” --class WordCount… -
接着第3步往下,看到了提交命令:yarnClient.submitApplication(appContext)
到此,Client已經向RM提交了申請,由RM指定一個NM來執行封裝的命令,啓動AM
ApplicationMaster的啓動
所以我們接下來的代碼應該從上面的command中:org.apache.spark.deploy.yarn.ApplicationMaster
-
先找到main方法,那既然是來啓動AM的,所以就先創建一個AM,並且執行了master.run()
-
接下來看run方法,大多數都是變量的賦值,其中包括創建HDFS文件系統:val fs = FileSystem.get(yarnConf)
-
並且找到Driver的執行:runDriver(securityMgr)
-
在Driver方法中,啓動了用戶類線程:userClassThread = startUserApplication()
-
用戶類線程中:用類加載器的方式來加載用戶類的main方法,並且,爲這個線程設置名稱爲"Driver",驗證了在yarn的cluster模式下,Driver運行在集羣(在client模式下,Driver運行在客戶端)
-
如圖第4步中,除了用戶類線程,還有向RM註冊AM
-
現在有了AM,得需要向RM申請資源:allocator.allocateResources(),這裏的方法名是分配資源:爲RM爲AM分配資源,也就是我說的申請資源
-
既然申請資源,肯定得獲取資源容器, 判斷申請到的資源容器大小是不是大於0,也就是說如果RM沒資源了,返回的肯定是個空的容器,如果>0,就進行處理這些資源:
-
對獲取到的資源進行分類(同一機架還是什麼情況)
-
啓動runAllocatedContainers(containersToUse),就是在這個Container裏面運行ExecutorBackend
-
遍歷可用的資源容器,對每一個進行如下操作:
for (container <- containersToUse) {
launcherPool.execute{new ExecutorRunnable().run()},找到了run方法
-
真正啓動Container
-
那startContainer()這個啓動方法進去,看一下是怎麼啓動的
ctx對命令進行了設置,ctx.setCommands(commands.asJava)
並且讓NMClient開始運行 容器:nmClient.startContainer(container.get, ctx)
其中的準備命令爲:val commands = prepareCommand(),點進去發現和之前的Client中的第4步有點類似。
命令是這樣的:
/bin/java org.apache.spark.executor.CoarseGrainedExecutorBackend
以上是Yarn Cluster模式下,從Spark-submit提交任務開始,到Excutor執行的全過程
那我們再次返回來看這張流程圖,是不是覺得前面的部分已經比較熟悉了。
下一篇我們能繼續看ExecutorBackend以及任務的劃分,下一篇再見。
最後再來一波,喜歡我的歡迎點贊,歡迎關注我的公衆號:後來X,回覆:spark源碼,獲取spark2.1.1源碼包
持續更新,未完待續!