Hadoop RPC機制詳解
阿新 • • 發佈:2019-08-29
網路通訊模組是分散式系統中最底層的模組,他直接支撐了上層分散式環境下複雜的程序間通訊邏輯,是所有分散式系統的基礎。遠端過程呼叫(RPC)是一種常用的分散式網路通訊協議,他允許運行於一臺計算機的程式呼叫另一臺計算機的子程式,同時將網路的通訊細節隱藏起來,使得使用者無需額外地為這個互動作用程式設計,大大的簡化了分散式程式開發
作為一個分散式檔案系統,Hadoop實現了自己的RPC通訊協議,他是上層多個分散式子系統(MapReduce,Yarn,HDFS等)公用的網路通訊模組
目錄 一. RPC通訊模型 二. Hadoop RPC的特點概述 三. RPC總體架構 四. Hadoop RPC使用方法 五. Hadoop RPC類詳解 六. Hadoop RPC引數調優
一. RPC通訊模型 RPC是一種提供網路從遠端計算機上請求服務,但不需要了解底層網路技術的協議 RPC通常採用客戶機/伺服器模型。請求程式是一個客戶機,而服務提供程式則是一個伺服器。一個典型的RPC框架,主要包括以下幾個部分 :
目錄 一. RPC通訊模型 二. Hadoop RPC的特點概述 三. RPC總體架構 四. Hadoop RPC使用方法 五. Hadoop RPC類詳解 六. Hadoop RPC引數調優
一. RPC通訊模型 RPC是一種提供網路從遠端計算機上請求服務,但不需要了解底層網路技術的協議 RPC通常採用客戶機/伺服器模型。請求程式是一個客戶機,而服務提供程式則是一個伺服器。一個典型的RPC框架,主要包括以下幾個部分 :
- 通訊模組。兩個相互協作的通訊模組實現請求 - 應答協議,它們在客戶和伺服器之間傳遞請求和應答訊息,一般不會對資料包進行任何處理。請求 - 應答協議的實現一般有同步方式和非同步方式兩種
- 同步模式下客戶端程式一直阻塞到伺服器斷髮送的應答請求到達本地
- 非同步模式下將請求傳送到服務端後,不必等待應答返回,可以做其他事情
- Stub程式。客戶端和伺服器端均包含Stub程式,可以將之看作代理程式。它使得遠端函式呼叫表現的跟本地呼叫一樣,對使用者程式完全透明。在客戶端,Stub程式像一個本地程式,但不直接執行本地呼叫,而是將請求資訊提供網路模組傳送給伺服器端,伺服器端給客戶端傳送應答後,客戶端Stub程式會解碼對應結果。在伺服器端,Stub程式依次進行解碼請求訊息中的引數、呼叫相應的服務過程和編碼應答結果的返回值等處理
- 排程程式。排程程式接收來自通訊模組的請求資訊,並根據其中的標識選擇一個Stub程式進行處理。通常客戶端併發請求量比較大時,會採用執行緒池提高處理效率
- 客戶程式/服務過程。請求的發出者和請求的處理者
同步模式和非同步模式對比 一個RPC請求從傳送到獲取處理結果,所經歷的步驟 :
- 客戶程式以本地方式呼叫系統產生的Stub程式
- 該Stub程式將函式呼叫資訊按照網路通訊模組的要求封裝成訊息包,並交給通訊模組傳送給遠端伺服器端
- 遠端伺服器端接收此訊息後,將此訊息傳送給相應的Stub程式
- Stub程式拆封訊息,形成被調過程要求的形式,並呼叫對應函式
- 被呼叫函式按照所獲引數執行,並將結果返回給Stub程式
- Stub程式將此結果封裝成訊息,通過網路通訊模組逐級地傳送給客戶程式
- 透明性。這是所有RPC框架最根本的特點,即當用戶在一臺計算機的程式呼叫另外一臺計算機上的子程式時,使用者自身不應感覺到其間設計機器間的通訊,而是感覺像是在執行一個本地呼叫
- 高效能。Hadoop各個系統(HDFS,YARN,MapReduce等)均採用了Master/Slave架構,其中,Master實際上是一個RPC Server,它負責處理叢集中所有Slave傳送的服務請求,為了保證Master的併發處理能力,RPC Server應是一個高效能伺服器,能夠高效地處理來自多個Client的併發RPC請求
- 可控性。RPC是Hadoop最底層最核心的模組之一,保證其輕量級,高效能和可控性顯得尤為重要
- 序列化層。序列化主要作用是將結構化物件轉為位元組流以便於通過網路進行傳輸或寫入持久儲存,在RPC框架中,它主要是用於將使用者請求中的引數或者應答轉換成位元組流以便跨機器傳輸
- 函式呼叫層。函式呼叫層主要功能是定位要呼叫的而函式並執行該函式,Hadoop RPC採用了Java反射機制與動態代理實現了函式呼叫
- 網路傳輸層。網路傳輸層描述了Client與Server之間訊息傳輸的方式,Hadoop RPC採用了基礎TCP/IP的Socket機制
- 伺服器端處理框架。伺服器端處理框架可被抽象為網路I/O模型,它描述了客戶端與伺服器間資訊的互動方式,它的設計直接決定這伺服器端的併發處理能力,而Hadoop RPC採用了基於Reactor設計模式的事件驅動I/O模型
Hadoop RPC總體架構
Hadoop RPC總體架構自下而上可分為兩層,第一層是一個基於Java NIO實現的客戶機 - 伺服器通訊模型。其中,客戶端將使用者的呼叫方法及其引數封裝成請求包後傳送到伺服器端。伺服器端收到請求包後,經解包、呼叫引數、打包結果等一系列操作後,將結果返回給客戶端。為了增強Server端的擴充套件性和併發處理能力,Hadoop RPC採用了基於事件驅動的Reactor設計模式,在具體實現時,用到了JDK提供的各種功能包,主要包括java.nio、java.lang.reflect(反射機制和動態代理)、java.net(網路程式設計)等。第二層是供更上層程式直接呼叫的RPC介面,這些介面底層即為C/S通訊模型 四. Hadoop RPC使用方法 Hadoop RPC對外主要提供了兩種介面(見類org.apache.hadoop.ipc.RPC),分別是 :- public static <T>ProtocolProxy <T> getProxy/waitForProxy() : 構造一個客戶端代理物件,用於向伺服器傳送RPC請求
- public static Server RPC.Builder (Configuration).build() : 為某個協議例項構造一個伺服器物件,用於處理客戶端傳送的請求
interface ClientProtocol extends org.apache.hadoop.ipc.VersionedProtocol { public static final long versionID = 1L; String echo(String value) throws IOException; int add(int v1 , int v2) throws IOException; }
(2) 實現RPC協議 Hadoop RPC協議通常是一個Java介面,使用者需要實現該介面
public static class ClientProtocolImpl implements ClientProtocol { //過載的方法,用於獲取自定義的協議版本號 public long getProtocolVersion(String protocol, long clientVersion) { return ClientProtocol.versionID; } //過載的方法,用於獲取協議簽名 public ProtocolSignature getProtocolSignature(String protocol, long clientVersion, inthashcode) { return new ProtocolSignature(ClientProtocol.versionID, null); } public String echo(String value) throws IOException { return value; } public int add(int v1, int v2) throws IOException { return v1 + v2; } }
(3) 構造並啟動RPC Server 直接使用靜態類Builder構造一個RPC Server,並呼叫函式start()啟動該Server
Server server = new RPC.Builder(conf).setProtocol(ClientProtocol.class) .setInstance(new ClientProtocolImpl()).setBindAddress(ADDRESS).setPort(0) .setNumHandlers(5).build(); server.start(); // BindAddress : 伺服器的HOST // Port : 監聽埠號,0代表系統隨機選擇一個埠號 // NumHandlers : 服務端處理請求的執行緒數目
(4) 構造RPC Client併發送RPC請求 使用靜態方法getProxy構造客戶端代理物件
proxy = (ClientProtocol)RPC.getProxy( ClientProtocol.class, ClientProtocol.versionID, addr, conf); int result = proxy.add(5, 6); String echoResult = proxy.echo("result");五. Hadoop RPC類詳解 Hadoop RPC主要有三個大類組成,即RPC、Client、Server,分別對應對外程式設計介面、客戶端實現、伺服器實現 ipc.RPC實現 RPC類實際上是對底層客戶機 - 伺服器網路模型的封裝,以便為程式設計師提供一套更方便簡潔的程式設計介面 RPC類定義了一系列構建和銷燬RPC客戶端的方法,構建方法分為getProxy和waitForProxy兩類,銷燬方只有一個,即為stopProxy。RPC伺服器的構建則由靜態內部類RPC.Builder,該類提供了一些方法共使用者設定一些基本的引數,設定完成引數,可呼叫build()完成一個伺服器物件的構建,呼叫start()方法啟動該伺服器 ipc.Client Client主要完成的功能是傳送遠端過程呼叫資訊並接收執行結果。 Client內部有兩個重要的內部類,分別是 :
- Call類 : 封裝了一個RPC請求,它包含5個成員變數,分別是唯一標識ID、函式呼叫資訊param、函式執行返回值value、出錯或者異常資訊error和執行完成識別符號done。由於Hadoop RPC Server採用非同步方式處理客戶端請求,這使遠端過程呼叫的發生順序與結果返回順序無直接關係,而Client端正式提供ID識別不同的函式呼叫的。當客戶端向伺服器端傳送請求時,只需填充id和param兩個變數,而剩下的三個變數則由伺服器根據函式執行情況填充
- Connection類 : Client與每個Server之間維護一個通訊連線,與該連線相關的基本資訊及操作被封裝到Connection類中,基本資訊主要包括通訊連線唯一標識、與Server端通訊的Socket、網路輸入資料流(in)、網路輸出資料流(out)、儲存RPC請求的雜湊表(calls)等。操作則包括 :
- addCall -- 將一個Call物件新增到雜湊表中
- sendParam -- 向伺服器端傳送RPC請求
- receiveResponse -- 從伺服器端接收已經處理完成的RPC請求
- run -- Connection是一個執行緒類,它的run方法呼叫了receiveResponse方法,會一直等待接收RPC返回結果
- 建立一個Connection物件,並將遠端方法呼叫資訊封裝成Call物件,放到Connection物件中的雜湊表中
- 呼叫Connection類中的sendRpcRequest()方法將當前Call物件傳送給Server端
- Server端處理完RPC請求後,將結果通過網路返回給Client端,Client端通過receiveRpcResponse()函式獲取結果
- Client檢查結果處理狀態,並將對應Call物件從雜湊表中刪除
Hadoop RPC Client處理過程 ipc.Server Hadoop採用了Master/Slave結構,其中Master是整個系統的單點,這是制約系統性能和可擴充套件性的最關鍵因素之一 ipc.Server採用了很多提高併發處理能力的技術,主要包括執行緒池、事件驅動和Reactor設計模式等 Reactor是併發程式設計中一種基於事件驅動的設計模式,它具有以下兩個特點 :
- 通過派發/分離IO操作事件提高系統的併發效能
- 提供了粗粒度的併發控制,使用單執行緒實現,避免了複雜的同步處理
- Reactor : I/O事件的派發者
- Acceptor : 接受來自Client的連線,建立與Client對應的Handler,並向Reactor註冊此Handler
- Handler : 與一個Client通訊的實體,並按一定的過程實現業務的處理
- Reader/Sender : 為了加速處理速度,Reactor模式往往構建一個存放資料處理執行緒的執行緒池,這樣資料讀出後,立即扔到執行緒吃中等待後續處理即可。為此,Reactor模式一般分離Handler中的讀和寫兩個過程,分別註冊成單獨的讀事件和寫事件,並由對應的Reader和Sender執行緒處理
Hadoop RPC Server處理過程 ipc.Server處理過程被劃分成3個階段 : 接收請求、處理請求和返回結果 接收請求 該階段主要任務是接收來自各個客戶端的RPC請求,並將它們封裝成固定的格式(Call類)放到一個共享佇列(CallQueue)中,該階段內部又分為建立連線和接收請求兩個子階段,分別由Listener和Reader兩種執行緒完成 整個Server只有一個Listener執行緒,統一負責監聽來自客戶端的連線請求,一旦由新的請求到達,它會採用輪詢的方式從執行緒池中選擇一個Reader執行緒進行處理,而Reader執行緒可同時存在多個,它們分別負責接收一部分客戶端連線的RPC請求,至於每個Reader執行緒負責哪些客戶端連線,完全由Listener決定,當前Listener只是採用了簡單的輪詢分配機制 Listener和Reader執行緒內部各自包含一個Selector物件,分別用於監聽SelectionKey.OP_ACCEPT和SelectionKey.OP_READ事件。對於Listener執行緒,主迴圈的實現體是監聽是否有新的連線請求到達,並採用輪詢策略選擇一個Reader執行緒處理新連線;對於Reader執行緒,主迴圈的實現體是監聽客戶端連線中是否有新的RPC請求到達,並將新的RPC請求封裝成Call物件,放到共享佇列中 處理請求 該階段主要任務是從共享佇列中獲取call物件,執行對應的函式呼叫,並將結果返回給客戶端,這全部由Handler執行緒完成 Server端可同時存在多個Handler執行緒,它們並行從共享佇列中讀取Call物件,經執行對應的函式呼叫後,將嘗試著直接將結果返回給對應的客戶端。但考慮到某些函式呼叫返回結果很大或者網路速度很慢,可能難以將結果一次性發送給客戶端,此時Handler將嘗試著將後續傳送任務交給Responder執行緒 返回結果 Server僅存一個Responder執行緒,它的內部包含一個Selector物件,用於監聽SelectionKey.OP_WRITE事件。當Handler沒能將結果一次性發送到客戶端時,會向該Selector物件註冊SelectionKey.OP_WRITE事件,進而由Responder執行緒採用非同步方式繼續傳送未傳送完成的結果 六. Hadoop RPC引數調優 HadoopRPC對外提供一些可配置引數,以便於使用者根據業務需求和硬體環境對其進行調優 :
- Reader執行緒數目。引數ipc.server.read.threadpool.size設定
- 每個Handler執行緒對應的最大Call數目。引數ipc.server.handler.queue.size設定
- Handler執行緒數目。引數yarn.resourcemanager.resource-tracker.client.thread-count和dfs.namenode.service.handler.count設定
- 客戶端最大重試次數。引數ipc.client.connect.max.size設定