By云端上的男人—DT大數據夢工廠
上一次筆者大致闡述了一下DAGScheduler中Stage的劃分,這次筆者將會闡述一下關于一個Job作業在提交的過程中所涉及到的一些參與相關的實體,如圖所示是Driver端Job提交的流程圖。
筆者所寫的博客面對的人員是有scala基礎以及對進程和線程有相關概念的人員和spark會基本使用的人員,如果大家有的對scala不是很熟悉的話,請大家觀看家林老師的相關的scala教程鏈接:鏈接:http://pan.baidu.com/s/1c2irB9I 密碼:t2bf
Spark基礎相關的鏈接鏈接:鏈接:http://pan.baidu.com/s/1dEG5m7J 密碼:atdx
如圖所示,job提交大致劃分為10個步驟。
1:用戶程序有action算子觸發一系列的語句,都是可以看成一個job。例子如下
sc.parallelize(1to100,4).map( ( _ ,1) ).reduceByKey( _ + _ ,3).collect
(注:一個用戶程序可以有多個Job,而且在用戶程序中生成的Stage可以被多個Job使用,不過筆者在這里目前不討論這些情況,只討論最簡潔的使用即可)
2:由1提交的語句除去action算子,然后把所有的生成的RDD提交給DAGScheduler實體,并按照寬依賴進行Stage劃分。如圖所示,兩個ShuffleMapStage,一個ResultStage。(這些內容讀者可以閱讀筆者的上次博客)。
3:把生成的Stage提交給TaskSchedulerImpl實體,TaskSchedulerImpl實體會把Stage封裝在一個TaskSetManager實體中,然后TaskSchedulerImpl實體會經過圖中4步驟會把已封裝TaskSetManager的實體放入到Pool實體中。這個Pool實體則是代表著真正管理所有的Stage。也就是說,在每個Stage之下,Spark又把Stage劃分為多個Task。這些Task又分為ShuffleMapTask和ResultTask兩種。如上圖所示。
4:如步驟3中所示,但是筆者在這里只考慮的是FIFO模式(即TaskSetManager是按照)。關于FAIR公平調度模式,讀者可以自行搜索。FIFO意義在于多個Job之間的運行是先入先出,即先到的Job,先獲得資源,然后運行完之后在第二個Job開始運行,以此類推,Stage之間因為有依賴,所以其計算也必須有依賴(一般情況),所以在真正把計算任務提交的時候,就需要約束這個條件,即先計算依賴的Stage的任務(Task),然后再計算當前這個Stage所代表的Task。
5:把生成的Task傳遞給CoarseGrainedSchedulerBackend實體,該實體通過步驟6負責把Task任務傳遞給driver所對應的還活著的計算節點中。
6:筆者之前闡述過RpcEndPoint相關的知識,而CoarseGrainedSchedulerBackend中存在著這樣的一個實體。該實體則會與外部 的RpcEndPoint實體通信,通過Netty框架把我們的Task任務傳輸過去。
7:既然我們的計算集群是計算任務的,那么想必就必須有結果返回給Driver(注:這里的Driver就是CoarseGrainedSchedulerBackend才對。所以該步驟就是要把計算后的結果返回給Driver。但是筆者在閱讀源碼的時候看到,計算節點返回的結果因Task任務運行的狀態的不同是返回不同的結果。或者說是一類是真正用戶需要的結果,則一類則是計算失敗返回的失敗結果的原因。不過慢慢的筆者也理解這里面所涉及的思路,如果是正常把結果返回了,框架的確并不要關注其他的事情。只有到返回的結果是失敗的內容的時候,框架就需要考慮容錯。這種思路是很常見。
8:由Driver接收到的結果路由到TaskSchedulerImpl實體來幫忙做處理。Driver
實體只是處理一些在其實體中的元數據的信息,然后如步驟7所說的,根據返回結果的不同讓TaskSchedulerImpl實體做不同的處理。
9:其實TaskSchedulerImpl實體只是處理一些在其實體中的元數據的信息而已,真正做結果的處理是路由到TaskResultGetter實體。如上所說,接收到結果一類是真正的數據,另一類則是任務的失敗的一些信息。
10:9把接收的數據的結果處理完后,又把控制權交給了TaskSchedulerImpl實體,該實體會根據9得到的結果的類型來做相應的不同的處理的步驟。
。。。。還有好多細節的步驟,筆者并不能一一細說,希望讀者自己嘗試著試試。或者筆者以后有時間我們在繼續這底層的細節。
接下來,我們看一下Driver把Task任務傳遞到CoarseGrainedExecutorBackend實體端流程,即我們把視角轉到了計算節點中的某一個實體來討論。
1:和上面圖中的最后的一步做銜接,CoarseGrainedSchedulerBackend實體把Task任務傳遞到集群某個CoarseGrainedExecutorBackend實體的計算節點中以便于計算。
2:和上面某些地方類似CoarseGrainedExecutorBackend實體本身就是一個RpcEndpoint實體,是用于接收和處理消息來使用的,也就是說CoarseGrainedExecutorBackend實體把真正要計算的事情交給了Exeutor實體來做計算。
3:如果讀者熟悉Java中線程池技術的話,或許會知道一個接口即:
在java中,這樣的接口具體實現類,以及相應的工廠方法我們更為的熟悉,即Executors工廠類,在Saprk中,我們也看到了一個類,而且名字是一樣,圖4所示。這兩個類的思想極為相似,即把任務提交給Executor實體(或者是其繼承類),然后讓其中的某個線程來執行所需要的結果。而在我們的Spark中,是通過使用TaskRunner在此封裝來做任務的計算,而真正要計算的還是Driver(在這里CoarseGrainedSchedulerBackend是主要作用)傳遞過來的Task。
4:這一步驟就是TaskRunner把Task計算好的任務的結果返回給Driver,在這里筆者需要說明說明的是,Task計算的任務 有可能成功,也有一些則不會 ,所以有了兩類結果。即之前所說的是Task計算后的結果,一類則是Task計算失敗需要返回的失敗原因的結果。
5:這一步則是依靠CoarseGrainedExecutorBackend實體,把計算結果反饋給我們的Driver,以便于Driver本身的處理。