Spark數據傳輸及ShuffleClient(源碼閱讀五)

  我們都知道Spark的每個task運行在不同的服務器節點上,map輸出的結果直接存儲到map任務所在服務器的存儲體系中,reduce任務有可能不在同一臺機器上運行,所以需要遠程將多個map任務的中間結果fetch過來。那麼我們就來學習下shuffleClient。shuffleClient存在於每個exeuctor的BlockManager中,它不光是將shuffle文件上傳到其他executor或者下載到本地的客戶端,也提供了可以被其他exeuctor訪問的shuffle服務.當有外部的(其他節點)shuffleClient時,新建ExternalShuffleClient,默認爲BlockTransferService.那麼真正init的實現方法在NettyBlockTransferService中。

 

  

 

  如代碼中所示,抽象類blockTransferservice繼承自shuffleClientNettyBlockTransferService實現了shuffleClient的init抽象方法(竟然是java寫的)進行初始化提供服務。初始化的過程爲:創建NettyBlockRpcServer,構造TransportContext上下文,同時創建了clientFactory,最終創建了Netty服務器TransportServer,可修改屬性spark.blockManager.port改變TransportServer的端口。

  我們會有疑問,上面那一坨,是幹嘛的?我們都知道,map和reduce任務處於不同節點時,reduce任務需要從遠端fetch map任務的中間結果輸出,NettyBlockRpcServer提供打開,下載Block文件的功能(中間結果在backet中)。NettyBlockRpcServer爲了容錯,還會將數據備份到其他節點。在new 了之後會根據接收到的message消息,匹配是打開block還是上傳block進行容錯。如圖:

  

  在new完NettyBlockRpcServer後,開始構造傳輸的上下文TransportContext.構造它的主要作用是,它將既可以創建Netty服務,也可以創建Netty訪問客戶端,主要包含:

  1、TransportConf,控制Netty框架提供的shuffle I/O交互的客戶端和服務端線程數量(又發現新的參數)。

  2、RpcHandler,負責shuffle的I/O服務端在接受到客戶端的RPC請求後,提供打開Block或者上傳Block的RPC處理,就是剛纔new的NettyBlockRpcServer,可以看到receive。

  3、decoder,在shuffle的I/O服務端對客戶端傳來的ByteBuf進行解析,防止丟包和解析錯誤

  4、encoder,在shuffle的I/O客戶端對消息內容進行編碼,防止服務端丟包和解析錯誤。

  

  那麼爲什麼需要decoder、encoder呢,這裏要補習下傳輸原理,一般基於TCP/IP的流傳輸中,接收到的數據首先會被存儲到一個socket緩衝區中,基於流的傳輸並不是一個數據包的隊列,而是一個字節隊列。即使發送兩個獨立的數據包,操作系統也不會作爲2個消息處理,而作爲一連串的字節。也就是說 發送的數據可能是 ABC UID GDI ,應用程序讀取的時候數據很可能被分成了 AB CUID G DI,所以應該把接收到的數據整理成一個或多個有意義能讓程序的邏輯更好理解的數據。

  接下來,開始創建RPC客戶端工程ClientFactory,它主要:1、緩存客戶端列表。2、緩存客戶端連接。3、節點之間取數據的連接數,通過spark.shuffle.io.numConnectionsPerPeer來配置,默認爲1。4、客戶端channel被創建時使用的類,可以使用屬性spark.shuffle.io.mode來配置,默認爲NioSocketChannel.(NIO還沒仔細學習過,它的特點爲所有的原始類型提供(Buffer)緩存支持,字符集編碼解決方案,提供一個新的原始的I/O抽象Channel,支持鎖和內存映射文件的文件訪問接口;提供多路非阻塞的高伸縮性網絡I/O)

  最終,createServer,看不懂NIO,回頭惡補下。。

  

 

  那麼下來,到了最重要的環節,獲取遠程shuffle文件,也就是fetch數據的過程。這個過程就是之前上面NettyBlockTransferService中的fetchBlocks方法(在shuffle過程中,可以通過container日誌查看到fetch數據):

  

  可以從傳入的參數中看到,會傳入拉取節點的IP與PORT以及blockId信息,進行數據的拉取。

  那麼之前,我們提到的上傳shuffle文件,以便之前的拉取,也是先創建了Netty服務的客戶端,同時我們可以看到它進行了serializer序列化並轉化爲了array()數組。隨之將blockId、appId、execId等一起封裝,調用Netty客戶端的sendRpc方法將字節數組上傳,同時毀掉函數RpcResponse-CallBack根據RPC的結果更改了上傳狀態。如下代碼:

  今天到此爲止,開始敲代碼~

 

                  

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章