Spark Rpc通信源碼分析

Spark 1.6+推出了以RPCEnv、RPCEndpoint、RPCEndpointRef為核心的新型架構下的RPC通信方式。其具體實現有Akka和Netty兩種方式,Akka是基于Scala的Actor的分布式消息通信系統,Netty是由JBOSS提供的一個java開源框架。Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。

Rpc Environment(RpcEnv)是一個RpcEndpoints用于處理消息的環境,它管理著整個RpcEndpoints的聲明周期:(1)根據name或uri注冊endpoints(2)管理各種消息的處理(3)停止endpoints。RpcEnv必須通過工廠類RpcEnvFactory創建。

RpcEndpoint需要注冊到RpcEnv,RpcEnv處理從RpcEndpointRef或遠程節點發送過來的消息,然后把響應消息給RpcEndpoint。對于Rpc捕獲到的異常消息,RpcEnv將會用RpcCallContext.sendFailure將失敗消息發送給發送者,或者將沒有發送者、‘NotSerializableException’等記錄到日志中。同時,RpcEnv也提供了根據name或uri獲取RpcEndpointRef的方法。


Rpc、RpcEndpoint、RpcEndpointRef三者關系

1.RpcEnv源碼分析

1.根據RpcEndpoint返回RpcEndpointRef,具體實現在RpcEndpoint.self方法中,如果RpcEndpointRef不存在,將返回null

private[rpc] def endpointRef(endpoint: RpcEndpoint): RpcEndpointRef

2.根據RpcEndpoint的name注冊到RpcEnv中并返回它的一個引用RpcEndpointRef

def setupEndpoint(name: String, endpoint: RpcEndpoint): RpcEndpointRef


3.獲取RpcEndpointRef的方法

(1)通過url獲取RpcEndpointRef

//通過url異步獲取RpcEndpointRef

def asyncSetupEndpointRefByURI(uri: String): Future[RpcEndpointRef]

//通過url同步獲取RpcEndpointRef,這是一個阻塞操作

def setupEndpointRefByURI(uri: String): RpcEndpointRef = {

defaultLookupTimeout.awaitResult(asyncSetupEndpointRefByURI(uri))}

(2)根據systemName、address、endpointName獲取RpcEndpointRef,其實是將三者拼接為uri,根據uri獲取

//異步獲取

def asyncSetupEndpointRef(

systemName: String, address: RpcAddress, endpointName: String): Future[RpcEndpointRef] = {

asyncSetupEndpointRefByURI(uriOf(systemName, address, endpointName))}

//同步獲取

def setupEndpointRef(

systemName: String, address: RpcAddress, endpointName: String): RpcEndpointRef = {

setupEndpointRefByURI(uriOf(systemName, address, endpointName))

}

4.根據RpcEndpointRef停止RpcEndpoint

def stop(endpoint: RpcEndpointRef): Unit

5.等待直到RpcEnv退出

def awaitTermination(): Unit

6.RpcEndpointRef需要RpcEnv來反序列化,所以當反序列化RpcEndpointRefs的任何object時,應該通過該方法來操作

def deserialize[T](deserializationAction: () => T): T

2.RpcEndpoint源碼分析


RpcEndpoint定義了由消息觸發的一些函數,`onStart`, `receive` and `onStop`的調用是順序發生的。它的聲明周期是constructor -> onStart -> receive* -> onStop。注意,`receive`能并發操作,如果你想要`receive`是線程安全的,請使用ThreadSafeRpcEndpoint,如果RpcEndpoint拋出錯誤,它的`onError`方法將會觸發。它有51個實現子類,我們比較熟悉的是Master、Worker、ClientEndpoint等。

1.啟動RpcEndpoint處理任何消息

def onStart(): Unit = {}

2.停止RpcEndpoint

def onStop(): Unit = {}

3.處理RpcEndpointRef.send或RpcCallContext.reply方法,如果收到不匹配的消息,將拋出SparkException

def receive: PartialFunction[Any, Unit] = {

case _ => throw new SparkException(self + " does not implement 'receive'")}

4.處理RpcEndpointRef.ask方法,如果不匹配消息,將拋出SparkException

def receiveAndReply(context: RpcCallContext): PartialFunction[Any, Unit] = {

case _ => context.sendFailure(new SparkException(self + " won't reply anything"))}

5.當處理消息發生異常時

def onError(cause: Throwable): Unit = {

throw cause}

6.當遠程地址連接到當前的節點地址時觸發

def onConnected(remoteAddress: RpcAddress): Unit = {

}

7.當遠程地址連接斷開時觸發

def onDisconnected(remoteAddress: RpcAddress): Unit = {

}

8.當遠程地址和當前節點的連接發生網絡異常時觸發

def onNetworkError(cause: Throwable, remoteAddress: RpcAddress): Unit = {

// By default, do nothing.

}

3.RpcEndpointRef源碼分析

RpcEndpointRef是RpcEndpoint的一個遠程引用,是線程安全的。它有兩個實現子類:即AkkaRpcEndpointRef和NettyRpcEndpointRef。

1.發送單方面的異步消息

def send(message: Any): Unit

2.發送一個消息給RpcEndpoint.receiveAndReply并返回一個Future在指定的時間內接受響應,本方法值請求一次

def ask[T: ClassTag](message: Any, timeout: RpcTimeout): Future[T]

3.發送消息給RpcEndpoint并在默認的超時內得到結果,否則拋出SparkException,注意,本方法是一個阻塞操作可能消耗時間,所以不要早消息循環中調用它

def askWithRetry[T: ClassTag](message: Any): T = askWithRetry(message, defaultAskTimeout)

最后,畫圖說明一下兩者的消息傳遞的過程,RpcEndpointRef作為消息的主動者,RpcEndpoint作為消息的被動者

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

推薦閱讀更多精彩內容