轉載自https://github.com/lw-lin/CoolplaySpark
本系列內容適用範圍:
* 2017.07.11 update, Spark 2.2 全系列 √ (已發佈:2.2.0)
* 2017.10.02 update, Spark 2.1 全系列 √ (已發佈:2.1.0, 2.1.1, 2.1.2)
* 2016.11.14 update, Spark 2.0 全系列 √ (已發佈:2.0.0, 2.0.1, 2.0.2)
目錄
(1) ReceiverTracker 分發和監控 Receiver
(2) ReceiverTracker 作爲 RpcEndpoint
(3) ReceiverTracker 管理塊數據的 meta 信息
引言
我們在 [Spark Streaming 實現思路與模塊概述](Spark Streaming 實現思路與模塊概述.md) 給出了 模塊 3:數據產生與導入
的基本工作流程:
-
(1) 由
Receiver
的總指揮ReceiverTracker
分發多個 job(每個 job 有 1 個 task),到多個 executor 上分別啓動ReceiverSupervisor
實例; -
(2) 每個
ReceiverSupervisor
啓動後將馬上生成一個用戶提供的Receiver
實現的實例 —— 該Receiver
實現可以持續產生或者持續接收系統外數據,比如TwitterReceiver
可以實時爬取 twitter 數據 —— 並在Receiver
實例生成後調用Receiver.onStart()
。
(1)(2) 的過程由上圖所示,這時 Receiver
啓動工作已運行完畢。
接下來 ReceiverSupervisor
將在 executor 端作爲的主要角色,並且:
-
(3)
Receiver
在onStart()
啓動後,就將持續不斷地接收外界數據,並持續交給ReceiverSupervisor
進行數據轉儲; -
(4)
ReceiverSupervisor
持續不斷地接收到Receiver
轉來的數據:-
如果數據很細小,就需要
BlockGenerator
攢多條數據成一塊(4a)、然後再成塊存儲(4b 或 4c) -
反之就不用攢,直接成塊存儲(4b 或 4c)
-
這裏 Spark Streaming 目前支持兩種成塊存儲方式,一種是由
blockManagerskManagerBasedBlockHandler
直接存到 executor 的內存或硬盤,另一種由WriteAheadLogBasedBlockHandler
是同時寫 WAL(4c) 和 executor 的內存或硬盤
-
-
(5) 每次成塊在 executor 存儲完畢後,
ReceiverSupervisor
就會及時上報塊數據的 meta 信息給 driver 端的ReceiverTracker
;這裏的 meta 信息包括數據的標識 id,數據的位置,數據的條數,數據的大小等信息。 -
(6)
ReceiverTracker
再將收到的塊數據 meta 信息直接轉給自己的成員ReceivedBlockTracker
,由ReceivedBlockTracker
專門管理收到的塊數據 meta 信息。
這裏 (3)(4)(5)(6) 的過程是一直持續不斷地發生的,我們也將其在上圖裏標識出來。
上面的內容我們已經在 [Receiver 分發詳解](3.1 Receiver 分發詳解.md) 和 [Receiver, ReceiverSupervisor, BlockGenerator, ReceivedBlockHandler 詳解](3.2 Receiver, ReceiverSupervisor, BlockGenerator, ReceivedBlockHandler 詳解.md) 中介紹過了。
本文我們詳解的是 driver 端的 ReceiverTracker
和 ReceivedBlockTracker
ReceiverTracker 的全限定名是:org.apache.spark.streaming.scheduler.ReceiverTracker
ReceivedBlockTracker 的全限定名是:org.apache.spark.streaming.scheduler.ReceivedBlockTracker
ReceiverTracker 詳解
ReceiverTracker
在 Spark 1.5.0 版本里的代碼變動比較大,不過其主要功能還是沒怎麼改變,我們一一來看:
- (1)
ReceiverTracker
分發和監控Receiver
ReceiverTracker
負責Receiver
在各個 executor 上的分發- 包括
Receiver
的失敗重啓
- (2)
ReceiverTracker
作爲RpcEndpoint
ReceiverTracker
作爲Receiver
的管理者,是各個Receiver
上報信息的入口- 也是 driver 下達管理命令到
Receiver
的出口
- (3)
ReceiverTracker
管理已上報的塊數據 meta 信息
整體來看,ReceiverTracker
就是 Receiver
相關信息的中樞。
(1) ReceiverTracker 分發和監控 Receiver
ReceiverTracker
分發和監控 Receiver
的內容我們已經在 [Receiver 分發詳解.md](3.1 Receiver 分發詳解.md) 詳解過了,我們這裏總結一下。
在 ssc.start()
時,將隱含地調用 ReceiverTracker.start()
;而 ReceiverTracker.start()
最重要的任務就是調用自己的 launchReceivers()
方法將 Receiver
分發到多個 executor 上去。然後在每個 executor 上,由 ReceiverSupervisor
來分別啓動一個 Receiver
接收數據。這個過程用下圖表示:
而且在 1.5.0 版本以來引入了 ReceiverSchedulingPolicy
,是在 Spark Streaming 層面添加對 Receiver
的分發目的地的計算,相對於之前版本依賴 Spark Core 的 TaskScheduler
進行通用分發,新的 ReceiverSchedulingPolicy
會對 Streaming 應用的更好的語義理解,也能計算出更好的分發策略。
並且還通過每個 Receiver
對應 1
個 Job
的方式,保證了 Receiver
的多次分發,和失效後的重啓、永活。
從本小節 ReceiverTracker
分發和監控 Receiver
的角度,我們對以前版本的 Spark Streaming(以 1.4.0 爲代表)、和新版本的 Spark Streaming(以 1.5.0 爲代表)有總結對比:
Spark Streaming 1.4.0 | Spark Streaming 1.5.0 | |
Receiver 活性 | 不保證永活 | 無限重試、保證永活 |
Receiver 均衡分發 | 無保證 | round-robin 策略 |
自定義 Receiver 分發 | 很 tricky | 方便 |
(2) ReceiverTracker 作爲 RpcEndpoint
RpcEndPoint
可以理解爲 RPC 的 server 端,供 client 調用。
ReceiverTracker
作爲 RpcEndPoint
的地址 —— 即 driver 的地址 —— 是公開的,可供 Receiver
連接;如果某個 Receiver
連接成功,那麼 ReceiverTracker
也就持有了這個 Receiver
的 RpcEndPoint
。這樣一來,通過發送消息,就可以實現雙向通信。
1.5.0 版本以來,ReceiverTracker
支持的消息有 10 種,我們進行一個總結:
StopAllReceivers 消息 | 消息 | 解釋 |
ReceiverTracker 只接收、不回覆 |
StartAllReceivers 消息 | 在 ReceiverTracker 剛啓動時,發給自己這個消息,觸發具體的 schedulingPolicy 計算,和後續分發 |
RestartReceiver 消息 | 當初始分發的 executor 不對,或者 Receiver 失效等情況出現,發給自己這個消息,觸發 Receiver 重新分發 | |
CleanupOldBlocks 消息 | 當塊數據已完成計算不再需要時,發給自己這個消息,將給所有的 Receiver 轉發此 CleanupOldBlocks 消息 | |
UpdateReceiverRateLimit 消息 | ReceiverTracker 動態計算出某個 Receiver 新的 rate limit,將給具體的 Receiver 發送 UpdateRateLimit 消息 | |
ReportError 消息 | 是由 Receiver 上報上來的,將觸發 reportError() 方法向 listenerBus 擴散此 error 消息 | |
ReceiverTracker 接收並回復 |
RegisterReceiver 消息 | 由 Receiver 在試圖啓動的過程中發來,將回復允許啓動,或不允許啓動 |
AddBlock 消息 | 具體的塊數據 meta 上報消息,由 Receiver 發來,將返回成功或失敗 | |
DeregisterReceiver 消息 | 由 Receiver 發來,處理後,無論如何都返回 true | |
AllReceiverIds 消息 | 在 ReceiverTracker stop() 的過程中,查詢是否還有活躍的 Receiver | |
StopAllReceivers 消息 | 在 ReceiverTracker stop() 的過程剛開始時,要求 stop 所有的 Receiver;將向所有的 Receiver 發送 stop 信息 |
(3) ReceiverTracker 管理塊數據的 meta 信息
一方面 Receiver
將通過 AddBlock
消息上報 meta 信息給 ReceiverTracker
,另一方面 JobGenerator
將在每個 batch 開始時要求 ReceiverTracker
將已上報的塊信息進行 batch 劃分,ReceiverTracker
完成了塊數據的 meta 信息管理工作。
具體的,ReceiverTracker
有一個成員 ReceivedBlockTracker
,專門負責已上報的塊數據 meta 信息管理。
ReceivedBlockTracker 詳解
我們剛剛講到,ReceivedBlockTracker
專門負責已上報的塊數據 meta 信息管理,但 ReceivedBlockTracker
本身不負責對外交互,一切都是通過 ReceiverTracker
來轉發 —— 這裏 ReceiverTracker
相當於是 ReceivedBlockTracker
的門面(可參考 門面模式)。
在 ReceivedBlockTracker
內部,有幾個重要的成員,它們的關係如下:
streamIdToUnallocatedBlockQueues
- 維護了上報上來的、但尚未分配入 batch 的
Block
塊數據的 meta - 爲每個
Receiver
單獨維護一個 queue,所以是一個HashMap:receiverId → mutable.Queue[ReceivedBlockInfo]
- 維護了上報上來的、但尚未分配入 batch 的
timeToAllocatedBlocks
- 維護了上報上來的、已分配入 batch 的
Block
塊數據的 meta - 按照 batch 進行一級索引、再按照
receiverId
進行二級索引的 queue,所以是一個HashMap: time → HashMap
- 維護了上報上來的、已分配入 batch 的
lastAllocatedBatchTime
- 記錄了最近一個分配完成的 batch 是哪個
上面是用於狀態記錄的主要數據結構。對這些狀態存取主要是 4 個方法:
addBlock(receivedBlockInfo: ReceivedBlockInfo)
- 收到某個
Receiver
上報上來的塊數據 meta 信息,將其加入到streamIdToUnallocatedBlockQueues
裏
- 收到某個
allocateBlocksToBatch(batchTime: Time)
- 主要是
JobGenerator
在發起新 batch 的計算時,第一步就調用本方法 - 是將
streamIdToUnallocatedBlockQueues
的內容,以傳入的batchTime
參數爲 key,添加到timeToAllocatedBlocks
裏 - 並更新
lastAllocatedBatchTime
- 主要是
getBlocksOfBatch(batchTime: Time)
- 主要是
JobGenerator
在發起新 batch 的計算時,由DStreamGraph
生成 RDD DAG 實例時,將調用本方法 - 調用本方法查
timeToAllocatedBlocks
,獲得劃入本 batch 的塊數據元信息,由此生成處理對應塊數據的 RDD
- 主要是
cleanupOldBatches(cleanupThreshTime: Time, ...)
- 主要是當一個 batch 已經計算完成、可以把已追蹤的塊數據的 meta 信息清理掉時調用
- 將清理
timeToAllocatedBlocks
表裏對應cleanupThreshTime
之前的所有 batch 塊數據 meta 信息
這 4 個方法,和對應信息狀態的修改關係如下圖總結:
上面即是 ReceivedBlockTracker
的主體內容。
但我們還需要強調一點非常重要的內容,即 ReceivedBlockTracker
需要對 driver 進行容錯保障。也就是,如果 driver 失效,新起來的 driver 必須能夠通過 WAL 恢復出失效前的 ReceivedBlockTracker
狀態,具體的就需要包括 streamIdToUnallocatedBlockQueues
, timeToAllocatedBlocks
, lastAllocatedBatchTime
等內容,也即需要前面講的 4 個方法在修改 ReceivedBlockTracker
的狀態信息的時候,要首先寫入 WAL,才能在失效後從 WAL 恢復出相關信息。
有關 WAL 寫入和故障恢復的內容,我們將在 模塊 4:長時容錯
裏系統性的詳解。
總結
本文主要詳解了 driver 端的 Receiver
管理者 —— ReceiverTracker
—— 的主要功能:
- (1)
ReceiverTracker
分發和監控Receiver
ReceiverTracker
負責Receiver
在各個 executor 上的分發- 包括
Receiver
的失敗重啓
- (2)
ReceiverTracker
作爲RpcEndpoint
ReceiverTracker
作爲Receiver
的管理者,是各個Receiver
上報信息的入口- 也是 driver 下達管理命令到
Receiver
的出口
- (3)
ReceiverTracker
管理已上報的塊數據 meta 信息- 委託給自己的成員
ReceivedBlockManager
進行具體管理
- 委託給自己的成員