網絡通信模塊是分布式系統(tǒng)中最底層的模塊,他直接支撐了上層分布式環(huán)境下復雜的進程間通信邏輯,是所有分布式系統(tǒng)的基礎。遠程過程調用(RPC)是一種常用的分布式網絡通信協(xié)議崎淳,他允許運行于一臺計算機的程序調用另一臺計算機的子程序,同時將網絡的通信細節(jié)隱藏起來具篇,使得用戶無需額外地為這個交互作用編程拴事,大大的簡化了分布式程序開發(fā)
作為一個分布式文件系統(tǒng),Hadoop實現(xiàn)了自己的RPC通信協(xié)議鬼譬,他是上層多個分布式子系統(tǒng)(MapReduce,Yarn,HDFS等)公用的網絡通信模塊
目錄
一. RPC通信模型
二. Hadoop RPC的特點概述
三. RPC總體架構
四. Hadoop RPC使用方法
五. Hadoop RPC類詳解
六. Hadoop RPC參數(shù)調優(yōu)
一. RPC通信模型
RPC是一種提供網絡從遠程計算機上請求服務,但不需要了解底層網絡技術的協(xié)議
RPC通常采用客戶機/服務器模型。請求程序是一個客戶機逊脯,而服務提供程序則是一個服務器优质。一個典型的RPC框架,主要包括以下幾個部分 :
- 通信模塊军洼。兩個相互協(xié)作的通信模塊實現(xiàn)請求 - 應答協(xié)議巩螃,它們在客戶和服務器之間傳遞請求和應答消息,一般不會對數(shù)據(jù)包進行任何處理匕争。請求 - 應答協(xié)議的實現(xiàn)一般有同步方式和異步方式兩種
- 同步模式下客戶端程序一直阻塞到服務器斷發(fā)送的應答請求到達本地
- 異步模式下將請求發(fā)送到服務端后避乏,不必等待應答返回,可以做其他事情
- Stub程序甘桑∨钠ぃ客戶端和服務器端均包含Stub程序,可以將之看作代理程序跑杭。它使得遠程函數(shù)調用表現(xiàn)的跟本地調用一樣铆帽,對用戶程序完全透明。在客戶端德谅,Stub程序像一個本地程序锄贼,但不直接執(zhí)行本地調用锡移,而是將請求信息提供網絡模塊發(fā)送給服務器端扳抽,服務器端給客戶端發(fā)送應答后漠其,客戶端Stub程序會解碼對應結果诡渴。在服務器端,Stub程序依次進行解碼請求消息中的參數(shù)冯键、調用相應的服務過程和編碼應答結果的返回值等處理
- 調度程序惹盼。調度程序接收來自通信模塊的請求信息,并根據(jù)其中的標識選擇一個Stub程序進行處理惫确。通呈直ǎ客戶端并發(fā)請求量比較大時,會采用線程池提高處理效率
-
客戶程序/服務過程改化。請求的發(fā)出者和請求的處理者
一個RPC請求從發(fā)送到獲取處理結果掩蛤,所經歷的步驟 :
- 客戶程序以本地方式調用系統(tǒng)產生的Stub程序
- 該Stub程序將函數(shù)調用信息按照網絡通信模塊的要求封裝成消息包,并交給通信模塊發(fā)送給遠程服務器端
- 遠程服務器端接收此消息后陈肛,將此消息發(fā)送給相應的Stub程序
- Stub程序拆封消息揍鸟,形成被調過程要求的形式,并調用對應函數(shù)
- 被調用函數(shù)按照所獲參數(shù)執(zhí)行句旱,并將結果返回給Stub程序
- Stub程序將此結果封裝成消息阳藻,通過網絡通信模塊逐級地傳送給客戶程序
二. Hadoop RPC的特點概述
RPC實際上時分布式計算中C/S(Client/Server)模型的一個應用案例
對于Hadoop而言,RPC具有以下幾個特點 :
- 透明性。這是所有RPC框架最根本的特點谈撒,即當用戶在一臺計算機的程序調用另外一臺計算機上的子程序時腥泥,用戶自身不應感覺到其間設計機器間的通信,而是感覺像是在執(zhí)行一個本地調用
- 高性能啃匿。Hadoop各個系統(tǒng)(HDFS,YARN,MapReduce等)均采用了Master/Slave架構蛔外,其中,Master實際上是一個RPC Server溯乒,它負責處理集群中所有Slave發(fā)送的服務請求夹厌,為了保證Master的并發(fā)處理能力,RPC Server應是一個高性能服務器橙数,能夠高效地處理來自多個Client的并發(fā)RPC請求
- 可控性。RPC是Hadoop最底層最核心的模塊之一帅戒,保證其輕量級灯帮,高性能和可控性顯得尤為重要
三. RPC總體架構
Hadoop RPC主要分為四個部分,分別是序列化層逻住、函數(shù)調用層钟哥、網絡傳輸層和服務器端處理框架
具體實現(xiàn)機制 :
- 序列化層。序列化主要作用是將結構化對象轉為字節(jié)流以便于通過網絡進行傳輸或寫入持久存儲瞎访,在RPC框架中腻贰,它主要是用于將用戶請求中的參數(shù)或者應答轉換成字節(jié)流以便跨機器傳輸
- 函數(shù)調用層。函數(shù)調用層主要功能是定位要調用的而函數(shù)并執(zhí)行該函數(shù)扒秸,Hadoop RPC采用了Java反射機制與動態(tài)代理實現(xiàn)了函數(shù)調用
- 網絡傳輸層播演。網絡傳輸層描述了Client與Server之間消息傳輸?shù)姆绞郊酵撸琀adoop RPC采用了基礎TCP/IP的Socket機制
- 服務器端處理框架。服務器端處理框架可被抽象為網絡I/O模型写烤,它描述了客戶端與服務器間信息的交互方式翼闽,它的設計直接決定這服務器端的并發(fā)處理能力,而Hadoop RPC采用了基于Reactor設計模式的事件驅動I/O模型
Hadoop RPC總體架構自下而上可分為兩層洲炊,第一層是一個基于Java NIO實現(xiàn)的客戶機 - 服務器通信模型感局。其中,客戶端將用戶的調用方法及其參數(shù)封裝成請求包后發(fā)送到服務器端暂衡。服務器端收到請求包后询微,經解包、調用參數(shù)狂巢、打包結果等一系列操作后撑毛,將結果返回給客戶端。為了增強Server端的擴展性和并發(fā)處理能力隧膘,Hadoop RPC采用了基于事件驅動的Reactor設計模式代态,在具體實現(xiàn)時,用到了JDK提供的各種功能包疹吃,主要包括java.nio蹦疑、java.lang.reflect(反射機制和動態(tài)代理)、java.net(網絡編程)等萨驶。第二層是供更上層程序直接調用的RPC接口歉摧,這些接口底層即為C/S通信模型
四. Hadoop RPC使用方法
Hadoop RPC對外主要提供了兩種接口(見類org.apache.hadoop.ipc.RPC),分別是 :
- public static <T>ProtocolProxy <T> getProxy/waitForProxy() : 構造一個客戶端代理對象腔呜,用于向服務器發(fā)送RPC請求
- public static Server RPC.Builder (Configuration).build() : 為某個協(xié)議實例構造一個服務器對象叁温,用于處理客戶端發(fā)送的請求
使用Hadoop RPC分為以下4個步驟 :
(1) 定義RPC協(xié)議
RPC協(xié)議是客戶端和服務器端之間的通信接口,它定義了服務器端對外提供的服務接口核畴。如下所示膝但,我們定義一個ClientProtocol通信接口,聲明了echo和add兩個方法谤草。Hadoop中所有自定義RPC接口都需要繼承VersionedProtocol接口跟束,它描述了協(xié)議的版本信息
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) 實現(xiàn)RPC協(xié)議
Hadoop RPC協(xié)議通常是一個Java接口,用戶需要實現(xiàn)該接口
public static class ClientProtocolImpl implements ClientProtocol {
//重載的方法丑孩,用于獲取自定義的協(xié)議版本號
public long getProtocolVersion(String protocol, long clientVersion) {
return ClientProtocol.versionID;
}
//重載的方法冀宴,用于獲取協(xié)議簽名
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
直接使用靜態(tài)類Builder構造一個RPC Server,并調用函數(shù)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 : 監(jiān)聽端口號温学,0代表系統(tǒng)隨機選擇一個端口號
// NumHandlers : 服務端處理請求的線程數(shù)目
(4) 構造RPC Client并發(fā)送RPC請求
使用靜態(tài)方法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逃延,分別對應對外編程接口览妖、客戶端實現(xiàn)、服務器實現(xiàn)
ipc.RPC實現(xiàn)
RPC類實際上是對底層客戶機 - 服務器網絡模型的封裝真友,以便為程序員提供一套更方便簡潔的編程接口
RPC類定義了一系列構建和銷毀RPC客戶端的方法黄痪,構建方法分為getProxy和waitForProxy兩類,銷毀方只有一個盔然,即為stopProxy桅打。RPC服務器的構建則由靜態(tài)內部類RPC.Builder,該類提供了一些方法共用戶設置一些基本的參數(shù)愈案,設置完成參數(shù)挺尾,可調用build()完成一個服務器對象的構建,調用start()方法啟動該服務器
ipc.Client
Client主要完成的功能是發(fā)送遠程過程調用信息并接收執(zhí)行結果站绪。
Client內部有兩個重要的內部類遭铺,分別是 :
- Call類 : 封裝了一個RPC請求,它包含5個成員變量恢准,分別是唯一標識ID魂挂、函數(shù)調用信息param、函數(shù)執(zhí)行返回值value馁筐、出錯或者異常信息error和執(zhí)行完成標識符done涂召。由于Hadoop RPC Server采用異步方式處理客戶端請求,這使遠程過程調用的發(fā)生順序與結果返回順序無直接關系敏沉,而Client端正式提供ID識別不同的函數(shù)調用的果正。當客戶端向服務器端發(fā)送請求時,只需填充id和param兩個變量盟迟,而剩下的三個變量則由服務器根據(jù)函數(shù)執(zhí)行情況填充
- Connection類 : Client與每個Server之間維護一個通信連接秋泳,與該連接相關的基本信息及操作被封裝到Connection類中,基本信息主要包括通信連接唯一標識攒菠、與Server端通信的Socket迫皱、網絡輸入數(shù)據(jù)流(in)、網絡輸出數(shù)據(jù)流(out)辖众、保存RPC請求的哈希表(calls)等卓起。操作則包括 :
- addCall -- 將一個Call對象添加到哈希表中
- sendParam -- 向服務器端發(fā)送RPC請求
- receiveResponse -- 從服務器端接收已經處理完成的RPC請求
- run -- Connection是一個線程類,它的run方法調用了receiveResponse方法赵辕,會一直等待接收RPC返回結果
當調用Call函數(shù)執(zhí)行某個遠程方法時既绩,Client端需要及進行以下四個步驟 :
- 創(chuàng)建一個Connection對象概龄,并將遠程方法調用信息封裝成Call對象还惠,放到Connection對象中的哈希表中
- 調用Connection類中的sendRpcRequest()方法將當前Call對象發(fā)送給Server端
- Server端處理完RPC請求后,將結果通過網絡返回給Client端私杜,Client端通過receiveRpcResponse()函數(shù)獲取結果
- Client檢查結果處理狀態(tài)蚕键,并將對應Call對象從哈希表中刪除
ipc.Server
Hadoop采用了Master/Slave結構救欧,其中Master是整個系統(tǒng)的單點,這是制約系統(tǒng)性能和可擴展性的最關鍵因素之一
ipc.Server采用了很多提高并發(fā)處理能力的技術锣光,主要包括線程池笆怠、事件驅動和Reactor設計模式等
Reactor是并發(fā)編程中一種基于事件驅動的設計模式,它具有以下兩個特點 :
- 通過派發(fā)/分離IO操作事件提高系統(tǒng)的并發(fā)性能
- 提供了粗粒度的并發(fā)控制誊爹,使用單線程實現(xiàn)蹬刷,避免了復雜的同步處理
ipc.Server實際上實現(xiàn)了一個典型的Reactor設計模式,典型的Reactor設計模式中主要包括以下幾個角色 :
- Reactor : I/O事件的派發(fā)者
- Acceptor : 接受來自Client的連接频丘,建立與Client對應的Handler办成,并向Reactor注冊此Handler
- Handler : 與一個Client通信的實體,并按一定的過程實現(xiàn)業(yè)務的處理
- Reader/Sender : 為了加速處理速度搂漠,Reactor模式往往構建一個存放數(shù)據(jù)處理線程的線程池迂卢,這樣數(shù)據(jù)讀出后,立即扔到線程吃中等待后續(xù)處理即可桐汤。為此而克,Reactor模式一般分離Handler中的讀和寫兩個過程,分別注冊成單獨的讀事件和寫事件怔毛,并由對應的Reader和Sender線程處理
ipc.Server處理過程被劃分成3個階段 : 接收請求员萍、處理請求和返回結果
接收請求
該階段主要任務是接收來自各個客戶端的RPC請求,并將它們封裝成固定的格式(Call類)放到一個共享隊列(CallQueue)中,該階段內部又分為建立連接和接收請求兩個子階段馆截,分別由Listener和Reader兩種線程完成
整個Server只有一個Listener線程充活,統(tǒng)一負責監(jiān)聽來自客戶端的連接請求,一旦由新的請求到達蜡娶,它會采用輪詢的方式從線程池中選擇一個Reader線程進行處理混卵,而Reader線程可同時存在多個,它們分別負責接收一部分客戶端連接的RPC請求窖张,至于每個Reader線程負責哪些客戶端連接幕随,完全由Listener決定,當前Listener只是采用了簡單的輪詢分配機制
Listener和Reader線程內部各自包含一個Selector對象宿接,分別用于監(jiān)聽SelectionKey.OP_ACCEPT和SelectionKey.OP_READ事件赘淮。對于Listener線程,主循環(huán)的實現(xiàn)體是監(jiān)聽是否有新的連接請求到達睦霎,并采用輪詢策略選擇一個Reader線程處理新連接梢卸;對于Reader線程,主循環(huán)的實現(xiàn)體是監(jiān)聽客戶端連接中是否有新的RPC請求到達副女,并將新的RPC請求封裝成Call對象蛤高,放到共享隊列中
處理請求
該階段主要任務是從共享隊列中獲取call對象,執(zhí)行對應的函數(shù)調用,并將結果返回給客戶端戴陡,這全部由Handler線程完成
Server端可同時存在多個Handler線程塞绿,它們并行從共享隊列中讀取Call對象,經執(zhí)行對應的函數(shù)調用后恤批,將嘗試著直接將結果返回給對應的客戶端异吻。但考慮到某些函數(shù)調用返回結果很大或者網絡速度很慢,可能難以將結果一次性發(fā)送給客戶端喜庞,此時Handler將嘗試著將后續(xù)發(fā)送任務交給Responder線程
返回結果
Server僅存一個Responder線程诀浪,它的內部包含一個Selector對象,用于監(jiān)聽SelectionKey.OP_WRITE事件延都。當Handler沒能將結果一次性發(fā)送到客戶端時笋妥,會向該Selector對象注冊SelectionKey.OP_WRITE事件,進而由Responder線程采用異步方式繼續(xù)發(fā)送未發(fā)送完成的結果
六. Hadoop RPC參數(shù)調優(yōu)
HadoopRPC對外提供一些可配置參數(shù)窄潭,以便于用戶根據(jù)業(yè)務需求和硬件環(huán)境對其進行調優(yōu) :
- Reader線程數(shù)目春宣。參數(shù)ipc.server.read.threadpool.size設置
- 每個Handler線程對應的最大Call數(shù)目。參數(shù)ipc.server.handler.queue.size設置
- Handler線程數(shù)目嫉你。參數(shù)yarn.resourcemanager.resource-tracker.client.thread-count和dfs.namenode.service.handler.count設置
- 客戶端最大重試次數(shù)月帝。參數(shù)ipc.client.connect.max.size設置