前面在 Spark Streaming 實現思路與模塊概述 和 DStream 生成 RDD 實例詳解 裏我們分析了 DStreamGraph
和 DStream
具有能夠實例化 RDD
和 RDD
DAG
的能力,下面我們來看 Spark Streaming 是如何將其動態調度的。
在 Spark Streaming 程序的入口,我們都會定義一個 batchDuration
,就是需要每隔多長時間就比照靜態的 DStreamGraph
來動態生成一個
RDD DAG 實例。在 Spark Streaming 裏,總體負責動態作業調度的具體類是 JobScheduler
,在
Spark Streaming 程序在 ssc.start()
開始運行時,會生成一個 JobScheduler
的實例,並被
start() 運行起來。
Spark Streaming 的 Job 總調度者 JobScheduler
JobScheduler
是
Spark Streaming 的 Job 總調度者。
JobScheduler
有兩個非常重要的成員:JobGenerator
和 ReceiverTracker
。JobScheduler
將每個
batch 的 RDD DAG 具體生成工作委託給 JobGenerator
,而將源頭輸入數據的記錄工作委託給 ReceiverTracker
。
JobGenerator
維護了一個定時器,週期就是我們剛剛提到的 batchDuration
,定時爲每個
batch 生成 RDD DAG 的實例。 具體的,根據我們在 DStream 生成 RDD 實例詳解 中的解析,DStreamGraph.generateJobs(time)
將返回一個 Seq[Job]
,其中的每個 Job
是一個 ForEachDStream
實例的 generateJob(time)
返回的結果。
此時,JobGenerator
拿到了 Seq[Job]
後(如上圖 (2)
),就將其包裝成一個
JobSet(如上圖 (3)
),然後就調用JobScheduler.submitJobSet(jobSet)
來交付回
JobScheduler(如上圖 (4) )。
那麼 JobScheduler
收到 jobSet
後是具體如何處理的呢?我們看其實現:
這裏最重要的處理邏輯是 job
=> jobExecutor.execute(new JobHandler(job))
,也就是將每個 job 都在 jobExecutor 線程池中、用 new JobHandler 來處理。
JobHandler
先來看 JobHandler 針對 Job 的主要處理邏輯:
也就是說,JobHandler
除了做一些狀態記錄外,最主要的就是調用 job.run()
!這裏就與我們在
DStream 生成 RDD 實例詳解 裏分析的對應起來了: 在 ForEachDStream.generateJob(time)
時,是定義了 Job
的運行邏輯,即定義了 Job.func
。而在JobHandler
這裏,是真正調用了 Job.run()
、將觸發 Job.func
的真正執行!
Job 運行的線程池 jobExecutor
上面 JobHandler
是解決了做什麼的問題,本節 jobExecutor
是解決 Job
在哪裏做。
具體的,jobExecutor
是 JobScheduler
的成員:
也就是,ThreadUtils.newDaemonFixedThreadPool()
調用將產生一個名爲 "streaming-job-executor"
的線程池,所以,Job
將在這個線程池的線程裏,被實際執行 func
。
spark.streaming.concurrentJobs 參數
這裏 jobExecutor
的線程池大小,是由 spark.streaming.concurrentJobs
參數來控制的,當沒有顯式設置時,其取值爲 1
。
進一步說,這裏 jobExecutor
的線程池大小,就是能夠並行執行的 Job
數。而回想前文講解的DStreamGraph.generateJobs(time)
過程,一次
batch 產生一個 Seq[Job}
,裏面可能包含多個 Job
——
所以,確切的,*有幾個 *output 操作,就調用幾次 ForEachDStream.generatorJob(time)
,就產生出幾個 Job
**。
爲了驗證這個結果,我們做一個簡單的小測試:先設置 spark.streaming.concurrentJobs
= 10
,然後在每個 batch 裏做 2
次foreachRDD()
這樣的 output 操作:
在上面的設定下,我們很容易知道,能夠同時在處理的 batch 有 10
/ 2 = 5
個,其餘的 batch 的 Job
只能處於等待處理狀態。
下面的就是剛纔測試代碼的運行結果,驗證了我們前面的分析和計算:
Spark Streaming 的 JobSet, Job,與 Spark Core 的 Job, Stage, TaskSet, Task
最後,我們專門拿出一個小節,辨別一下這 Spark Streaming 的 JobSet, Job,與 Spark Core 的 Job, Stage, TaskSet, Task 這幾個概念。
Spark Core 的 Job, Stage, Task 就是我們“日常”談論 Spark 任務時所說的那些含義,而且在 Spark 的 WebUI 上有非常好的體現,比如下圖就是 1 個 Job
包含
3 個 Stage
;3
個 Stage
各包含
8, 2, 4 個 Task
。而 TaskSet
則是
Spark Core 的內部代碼裏用的類,是 Task
的集合,和 Stage
是同義的。
而 Spark Streaming 裏也有一個 Job
,但此 Job
非彼 Job
。Spark
Streaming 裏的 Job
更像是個 Java
裏的 Runnable
,可以 run()
一個自定義的 func
函數。而這個 func
,
可以:
- 直接調用
RDD
的 action,從而產生 1 個或多個 Spark Core 的Job
- 先打印一行表頭;然後調用
firstTen = RDD.collect()
,再打印firstTen
的內容;最後再打印一行表尾 —— 這正是DStream.print()
的Job
實現 - 也可以是任何用戶定義的 code,甚至整個 Spark Streaming 執行過程都不產生任何 Spark Core 的
Job
—— 如上一小節所展示的測試代碼,其Job
的func
實現就是:Thread.sleep(Int.MaxValue)
,僅僅是爲了讓這個Job
一直跑在jobExecutor
線程池裏,從而測試jobExecutor
的並行度 :)
最後,Spark Streaming 的 JobSet
就是多個 Job
的集合了。
如果對上面 5 個概念做一個層次劃分的話(上一層與下一層多是一對多的關係,但不完全準確),就應該是下表的樣子:
Spark Core | Spark Streaming | |
lv 5 | RDD DAGs | DStreamGraph |
lv 4 | RDD DAG | JobSet |
lv 3 | Job | Job |
lv 2 | Stage | ← |
lv 1 | Task | ← |
轉自:GitHub
https://github.com/proflin/CoolplaySpark/blob/master/Spark%20Streaming%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E7%B3%BB%E5%88%97/2.1%20JobScheduler,%20Job,%20JobSet%20%E8%AF%A6%E8%A7%A3.md
轉載請註明:人人都是數據咖 » JobScheduler, Job, JobSet 詳解