spark streaming原始碼分析3 排程及執行
前面的兩節內容介紹了StreamingContext的構造以及在此上的一系列操作。
通過呼叫start方法,真正開始排程執行。首先校驗狀態是否是INITIALIZED,然後呼叫JobScheduler的start方法,並將狀態設定為ACTIVE。
看一下JobScheduler的start方法內部
1、首先構造一個事件型別為[JobSchedulerEvent]的迴圈器eventLoop(包含JobStarted,JobCompleted,ErrorReported三個事件),內部有一個執行緒實時獲取佇列中的事件,有則處理。實際呼叫如上的onReceive/onError方法。eventLoop.start後,內部執行緒真正執行起來,並等待事件的到來。
2、構造ReceiverTracker
(1)從DStreamGraph中獲取註冊的ReceiverInputStreams
(2)獲取所有ReceiverInputStreams的streamId
(3)構造一個ReceiverLauncher,它是一個接受器
(4)構造一個ReceivedBlockTracker,
用於維護所有的接收器(receiver)接收到的所有block資訊,即ReceivedBlockInfo
3、呼叫receiverTracker的start方法。
如果receiverInputStreams不為空,則建立akka RPC服務,名稱為ReceiverTracker,負責註冊Receiver、AddBlock、ReportError(報告錯誤)、登出Receiver四個事件
呼叫receiverExecutor的start方法,最終呼叫了startReceivers方法。
1)獲取所有的receiver(接收器)
2)將receivers建立tempRDD,並分割槽並行化,每個分割槽一個元素,元素為receiver
3)建立方法startReceiver,該方法以分割槽元素(receiver)的迭代器作為引數,之後將該方法引數傳入runJob中,針對每個分割槽,依次將每個分割槽中的元素(receiver)應用到該方法上
4)runJob的startReceiver方法。每個分割槽只有一個receiver,因此在該方法內構造一個ReceiverSupervisorImpl,在它內部真正的接收資料並儲存。傳送RegisterReceiver訊息給dirver驅動。
重點介紹一下supervisor.start方法內部的邏輯實現:主要分為以下兩個方法
(1)onStart方法:
- 資料真正接收到是發生在SocketReceiver.receive函式中,將接收到的資料放入到BlockGenerator.currentBuffer
- 在BlockGenerator中有一個重複定時器,處理函式為updateCurrentBuffer, updateCurrentBuffer將當前buffer中的資料封裝為一個新的Block,放入到blocksForPush佇列中
- 同樣是在BlockGenerator中有一個BlockPushingThread,其職責就是不停的將blocksForPushing佇列中的成員通過pushArrayBuffer函式傳遞給blockmanager,讓BlockManager將資料儲存到MemoryStore中
- pushArrayBuffer還會將已經由BlockManager儲存的Block的id號傳遞給ReceiverTracker,ReceiverTracker會將儲存的blockId放到對應StreamId的佇列中
(2)startReceiver方法:
1)receiver.onStart方法
建立socket連線,逐行讀取資料,最終將資料插入BlockGenerator的currentBuffer中。一旦插入了資料,就觸發了上面重複定時器。按設定的block生產間隔(預設200ms),生成block,將block插入blocksForPushing佇列中。然後,blockPushingThread執行緒逐個取出傳遞給blockmanager儲存起來,同時通過AddBlock訊息通知ReceiverTracker已經將哪些block儲存到了blockmanager中。
2)onReceiverStart方法
向receiverTracker(位於driver端)傳送RegisterReceiver訊息,報告自己(receiver)啟動了,目的是可以在UI中反饋出來。ReceiverTracker將每一個stream接收到但還沒有進行處理的block放入到receiverInfo,其為一Hashmap. 在後面的generateJobs中會從receiverInfo提取資料以生成相應的RDD。
4、呼叫jobGenerator的start方法。
(1)首先構建JobGeneratorEvent型別事件的EventLoop,包含GenerateJobs,ClearMetadata,DoCheckpoint,ClearCheckpointData四個事件。並執行起來。
(2)呼叫startFirstTime啟動generator
timer.getStartTime計算出來下一個週期的到期時間,計算公式:(math.floor(clock.currentTime.toDouble / period) + 1).toLong * period,以當前的時間/除以間隔時間,再用math.floor求出它的上一個整數(即上一個週期的到期時間點),加上1,再乘以週期就等於下一個週期的到期時間。
(3) 啟動DStreamGraph,呼叫graph.start方法,啟動時間比startTime早一個時間間隔,為什麼呢?求告知!!!
(4) 呼叫timer.start方法,引數為startTime
這裡的timer為:
內部包含一個定時器,每隔batchDuration的時間間隔就向eventLoop傳送一個GenerateJobs訊息,引數longTime為下一個間隔到來時的時間點
通過內部的thread.start方法,觸發timer內部的定時器執行。從而按時間間隔產生job。
5、GenerateJobs/ClearMetadata 事件處理介紹
JobGeneratorEvent型別事件的EventLoop,包含GenerateJobs,ClearMetadata,DoCheckpoint,ClearCheckpointData四個事件
GenerateJobs:
(1)allocateBlocksToBatch:首先根據time的值獲取之前receiver接收到的並且通過AddBlock訊息傳遞給receiverTracker的block後設資料資訊。並且將time對應的blocks資訊對映儲存起來。
那麼,這裡的time是怎麼和每200ms間隔產生blocks對應起來的呢?答案就是time時間到後,將所有接收到但還未分配的blocks都劃為這個time間隔內的。
(2)generateJobs:根據一個outputStream生成一個job,最終每個outputStream都呼叫如下的方法,見下面程式碼註釋
注:這裡的generateJob實際呼叫的是根據outputStream過載的方法,比如print的方法是輸出一些值:
這裡需要解釋一下ReceiverInputDStream的compute方法
1)首先根據time值將之前對映的blocks後設資料資訊獲取出來
2) 獲取這些blocks的blockId,blockId其實就是streamId+唯一值,這個唯一值可以保證在一個流裡面產生的唯一的Id
3)將這個batchTime時間內的blocks元資訊彙總起來,儲存到inputInfoTracker中
4)將sparkContext和blockIds封裝成BlockRDD返回
至此,Job已經產生了。如果Job產生成功,就走Case Success(Jobs) =>分支
主要是根據time,jobs,以及streamId和每個streamId的記錄數的對映封裝成JobSet,呼叫submitJobSet
可以看到,將jobSet儲存到jobSets這樣一個對映結構當中,然後將每個job通過JobHandler封裝之後,通過一個執行緒呼叫執行起來。這個執行緒就是通過“spark.streaming.concurrentJobs”引數設定的一個執行緒池,預設是1。
接著看JobHandler被執行緒處理時的邏輯,見程式碼註釋:
ClearMetadata:
當一個jobset完成後,就會處理ClearMetadata訊息
1、根據time的時間,過濾出在time之前的rdd,如果設定了rememberDuration,則過濾出小於(time-rememberDuration)的rdd
2、將過濾出的rdd呼叫unpersist
3、刪除在blockManager中的block
4、根據dependencies關係鏈依次刪除,從outputStream開始,根據鏈路依次進行
5、刪除其它記憶體紀錄資訊
至此,關於spark stream最重要的部分,排程及執行就分析結束了!
轉載: http://blog.csdn.net/yueqian_zhu/article/details/49023383
相關文章
- Java排程執行緒池ScheduledThreadPoolExecutor原始碼分析Java執行緒thread原始碼
- spark streaming原始碼分析1 StreamingContextSpark原始碼GCContext
- libgo原始碼分析之多執行緒協程管理和排程Go原始碼執行緒
- spark streaming原始碼分析4 DStream相關APISpark原始碼API
- kube-scheduler原始碼分析(3)-搶佔排程分析原始碼
- TSM不能執行排程的原因分析及處理
- 原始碼分析OKHttp的執行過程原始碼HTTP
- knockout原始碼分析之執行過程原始碼
- RxJava原始碼解析(二)—執行緒排程器SchedulerRxJava原始碼執行緒
- Golang原始碼學習:排程邏輯(三)工作執行緒的執行流程與排程迴圈Golang原始碼執行緒
- Spark Streaming Backpressure分析Spark
- SpringMVC執行流程及原始碼分析SpringMVC原始碼
- spark core原始碼分析3 Master HASpark原始碼AST
- 【Spark篇】---Spark中資源和任務排程原始碼分析與資源配置引數應用Spark原始碼
- spark streaming執行kafka資料來源SparkKafka
- spark 原始碼分析之二十一 -- Task的執行流程Spark原始碼
- golang 原始碼分析之scheduler排程器Golang原始碼
- laravel 應用層執行過程原始碼分析Laravel原始碼
- 【Spark篇】---Spark資源排程和任務排程Spark
- spark streaming原始碼分析2 從簡單例子看DStream上的operationSpark原始碼單例
- Java併發和多執行緒3:執行緒排程和有條件取消排程Java執行緒
- Spark RPC框架原始碼分析(二)RPC執行時序SparkRPC框架原始碼
- spark排程管理Spark
- go 原始碼分析 goroutine 概覽與排程Go原始碼
- spark core原始碼分析1 叢集啟動及任務提交過程Spark原始碼
- Spark 以及 spark streaming 核心原理及實踐Spark
- 執行流程原始碼分析原始碼
- Spark 原始碼分析系列Spark原始碼
- Spring原始碼分析(七)SpringAOP生成代理類及執行的過程Spring原始碼
- Linux程式排程邏輯與原始碼分析Linux原始碼
- Spark中資源排程和任務排程Spark
- JUC(4)---java執行緒池原理及原始碼分析Java執行緒原始碼
- Spark報錯(二):關於Spark-Streaming官方示例wordcount執行異常Spark
- Linux 核心排程器原始碼分析 - 初始化Linux原始碼
- 執行緒池原始碼分析執行緒原始碼
- Mybatis執行流程原始碼分析MyBatis原始碼
- Spark原始碼分析之MemoryManagerSpark原始碼
- Spark原始碼分析之BlockStoreSpark原始碼BloC