9. Spark Streaming技術(shù)內(nèi)幕 : Receiver的精妙實現(xiàn)和生命周期研究和思考

原創(chuàng)文章,轉(zhuǎn)載請注明:轉(zhuǎn)載自聽風(fēng)居士博客(http://www.lxweimin.com/users/4435a13863fb/timeline)

Spark streaming 程序需要不斷接收新數(shù)據(jù),然后進行業(yè)務(wù)邏輯處理,而用于接受數(shù)據(jù)的就是Recever。顯然Receiver的正常運行對應(yīng)整個Spark Streaming應(yīng)用程序至關(guān)重要,如果Receiver出現(xiàn)異常,后面的業(yè)務(wù)邏輯就無從談起。Spark Streaming 是如何實現(xiàn)Receiver以保證其可靠性的,本文將結(jié)合Spark Streaming的Receiver源碼實現(xiàn)詳細解析Receiver的實現(xiàn)原理。

一、Receiver 實現(xiàn)策略思考

1、啟動Receiver的時候,啟動一個Job,這個Job里面有RDD的transformations操作和action的操作,這個Job只有一個partition.這個partition的特殊是里面只有一個成員,這個成員就是啟動的Receiver。這樣做的問題:

a) ?如果有多個InputDStream,那就要啟動多個Receiver,每個Receiver也就相當于分片partition,那我們啟動Receiver的時候理想的情況下是在不同的機器上啟動Receiver,但是Spark Core的角度來看就是應(yīng)用程序,感覺不到Receiver的特殊性,所以就會按照正常的Job啟動的方式來處理,極有可能在一個Executor上啟動多個Receiver.這樣的話就可能導(dǎo)致負載不均衡。

b) ?有可能啟動Receiver失敗,只要集群存在Receiver就不應(yīng)該失敗。

c) ?運行過程中,就默認的而言如果是一個partition的話,那啟動的時候就是一個Task,但是此Task也很可能失敗,因此以Task啟動的Receiver也會掛掉。

2、由Spark Streaming 自己管理Receiver,負責Receiver的調(diào)度和容錯和啟動。這樣做的好處:

a)由Spark Streaming調(diào)度Receiver 可以充分考慮負責均衡,避免將多個Receiver調(diào)度到同一臺機器上

b)Receiver 失敗后可以自動重新啟動,繼續(xù)接受數(shù)據(jù),從而使程序持續(xù)不斷繼續(xù)工作下去。

c)Receiver 重啟不收Task重啟次數(shù)的限制。

二、Spark Streaming的Receiver實現(xiàn)原理

2.1、和Receiver實現(xiàn)相關(guān)核心成員

(1)ReceiverTracker

(2)ReceiverTrackerEndpoint

(3)ReceiverSuperVisor

(4)Receiver

ReceiverTracker在Driver端,ReceiverSuperVisor和Receiver在Executor端,架構(gòu)圖如下:

2.2Spark Streaming的Receiver實現(xiàn)源碼解析

首先SparkStream啟動時候會啟動JobScheduler,在JobSceduler的start方法中,會實例化ReceiverTracker,并調(diào)用ReceiverTracker的start方法啟動ReceiverTracker。

ReceiverTracker的start方法首先檢查輸入流是否為空,如果不為空會創(chuàng)建ReceiverTrackerEndpoint并注冊給rpcEnv。然后調(diào)用launchReceivers方法啟動Receiver。其中receiverInputStreams是注冊到DStreamGraph中的ReceiverInputDStream。

下面看一下launchReceiver方法:基于ReceiverInputDStream(是在Driver端)來獲得具體的Receivers實例,然后再把他們分不到Worker節(jié)點上。一個ReceiverInputDStream只產(chǎn)生一個Receiver

首先從ReceiverInputDStream中獲取Receiver,然后調(diào)用runDummySparkJob啟動一個虛擬任務(wù),我們在后面再分析這個虛擬任務(wù),先看一下后面的核心代碼:

endpoint.send(StartAllReceivers(receivers))

此處的endpoint就是剛才實例化的ReceiverTrackerEndpoint對象的引用,可以看到此處給endpoint發(fā)送了StartAllReceivers消息。

下面看下一ReceiverTrackerEndpoint收到StartAllReceivers消息后的處理邏輯:

首先,根據(jù)一定的調(diào)度策略給傳入receivers分配相應(yīng)的executors,從這里可以看出,Receiver的調(diào)度并不是交給spark內(nèi)核完成的,而是由Spark Streaming框架完成調(diào)度過程。這樣做的目的就是為了避免Spark內(nèi)核將Receiver當做普通的job而將多個Receiver調(diào)度到同一個節(jié)點上。

Spark Streaming的調(diào)度策略這里不做分析,接著看下面的代碼,迭代所以的receiver ,對每個receiver調(diào)用 startReceiver方法在具體Executor上啟動Receiver。

這個startReceiver比較復(fù)雜,我們一步步分析,先看最核心的一行代碼:

可看到,Spark Streaming 為每個Receiver 啟動了一個job,而不是由Action操作出發(fā)Job執(zhí)行。

這里job的提交主要關(guān)注兩個參數(shù)receiverRDD和startReceiverFunc。

receiverRDD的源碼:

可以看到調(diào)用了SparkContext的makeRDD方法創(chuàng)建了RDD,該RDD只有一條數(shù)據(jù),就是receiver對象

下面看一看startReceiverFunc的源碼

startReceiverFunc 在worker節(jié)點上啟動receiver,首先創(chuàng)建了一個ReceiverSupervisiorImpl 對象 supervisor,然后調(diào)用supervisor的start方法在該節(jié)點上啟動supervisor:

ReceiverSupervisiorImpl 是繼承自ReceiverSupervisor,ReceiverSupervisor中調(diào)用了startReceiver方法:

首先調(diào)用onReceiverStart方法,將Receiver注冊給receiverTracker:

如果注冊成功,調(diào)用了Receiver的onStart方法在Executor啟動Receiver不斷接受數(shù)據(jù),并將接收的數(shù)據(jù)交給BlockManager管理,至此Receiver啟動完成。

回到ReceiverTracker的startReceiver方法,如果Receiver對應(yīng)的job完成,無論返回成功或失敗,只要ReceiverTracker還沒有停止就會發(fā)送RestartReceiver消息給ReceiverTrakerEndpoint,重啟Receiver。從這里可以看出Receiver不會像普通的spark core 程序一樣受到重試次數(shù)的限制而導(dǎo)致作業(yè)失敗

最后,在看一下runDummyJob方法:

該方法運行了一個簡單的wordcount程序,運行該程序的目的是確保所有slaves節(jié)點都被注冊了,讓receiver盡量分配到不同的work上運行,看一下getExecutors的源碼 :

總結(jié):Driver端的ReceiverTracker管理所有Executor上的Receiver任務(wù),他有一個ReceiverTrakerEndpoint 消息通訊體,這個消息通訊體在startReceiver方法中提交Receiver的job在具體Executor上運行,并接受Executor端發(fā)送過來的消息(比如注冊Receiver),在Executor端有一個ReceiverSupervisor專門管理Receiver,負責Receiver的注冊啟動與ReceiverTracker的信息交互。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容