Hadoop學習--HDFS的RPC通訊原理總結
這裡先寫下自己學習RPC的筆記總結,下面將詳細介紹學習過程:
RPC(remote procedure call)
不同java程序間的物件方法的呼叫。一方稱作服務端(server),一方稱作客戶端(client)。
server端提供物件,供客戶端呼叫的,被呼叫的物件的方法的執行發生在server端。
RPC是hadoop框架執行的基礎。
通過rpc小例子獲得的認識?
1. 服務端提供的物件必須是一個介面,介面extends VersioinedProtocal
2. 客戶端能夠的物件中的方法必須位於物件的介面中。
檢視一個基類或者介面的派生類或實現類---滑鼠指向類名,Ctrl + T ;
檢視函式的呼叫關係(找到所有呼叫該方法的函式)--Ctrl + Alt + H (ubuntu系統快捷鍵佔用,可以類名右鍵找open call Hierarchy,結果在控制檯輸出) ;
快速查詢類物件的相關資訊 -- Ctrl + O(查詢類名的所有成員變數和方法),F3檢視類名的定義。
RPC 是遠端過程呼叫(Remote Procedure Call),即遠端呼叫其他虛擬機器中執行的 java object。RPC 是一種客戶端/伺服器模式,那麼在使用時包括服務端程式碼和客戶端程式碼,還有我們呼叫的遠端過程物件。
HDFS 的執行就是建立在此基礎之上的。這裡通過分析實現一個簡單的 RPC 程式來分析HDFS 的執行機理。
1.首先定義遠端呼叫類的介面,介面繼承的 VersionedProtocal,是hadoop 的 RPC 的介面,所有的 RPC 通訊必須實現這個一介面,用於保證客戶端和服務端的埠一致。服務端被呼叫的類必須繼承這個介面 VersionedProtocal。
2.然後編寫遠端呼叫類,實現這個介面MyBizable,這裡面有兩個方法被實現,一個就是 hello方法,另一個是 getProtocalVersion 方法。package com.RPC; import org.apache.hadoop.ipc.VersionedProtocol; public interface MyBizable extends VersionedProtocol{ //定義抽象類方法hello public abstract String hello(String name); }
package com.RPC;
import java.io.IOException;
//實現介面MyBizable,重寫hello和getProtocolVersion方法
public class MyBiz implements MyBizable{
public static long BIZ_VERSION = 123456L;
@Override
public String hello(String name) {
System.out.println("我是ByBiz,我被呼叫了。");
return "hello" + name;
}
@Override
public long getProtocolVersion(String protocol, long clientVersion)
throws IOException {
//<span style="color:#FF0000;">返回BIZ_VERSION,保證伺服器和客戶端請求版本一致</span>
return BIZ_VERSION;
}
}
3.有了遠端呼叫物件,我們就可以編寫伺服器端程式碼,詳細在程式碼中有介紹。
package com.RPC;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.RPC.Server;
public class MyServer {
//定義final型別伺服器地址和埠
public static final String SERVER_ADDRESS = "localhost";
public static final int SERVER_PORT = 1234;
/**
* RPC是遠端過程呼叫(Remote Procedure Call)
*/
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
//重點RPC.getServer方法,該方法有四個引數,第一個引數是被呼叫的 java物件,
//第二個引數是伺服器的地址,第三個引數是伺服器的埠。獲得伺服器物件後,
//啟動伺服器。這樣,伺服器就在指定埠監聽客戶端的請求。
final Server server = RPC.getServer(new MyBiz(), SERVER_ADDRESS, SERVER_PORT, conf);
server.start();
}
}
4.最後,我們就可以編寫客戶端程式碼,來呼叫伺服器方法,注意方法在伺服器實現。
package com.RPC;
import java.net.InetSocketAddress;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
public class MyClient {
/**
* RPC客戶端
*/
public static void main(String[] args) throws Exception {
//RPC.getProxy(),該方法有四個引數,第一個引數是被呼叫的介面類,
//第二個是客戶端版本號,第三個是服務端地址。返回的代理物件,
//就是服務端物件的代理,內部就是使用 java.lang.Proxy 實現的。
final MyBizable proxy = (MyBizable) RPC.getProxy(MyBizable.class,
MyBiz.BIZ_VERSION, new InetSocketAddress(MyServer.SERVER_ADDRESS, MyServer.SERVER_PORT) ,new Configuration() );
//呼叫介面中的方法
final String result = proxy.hello("world");
//列印返回結果,然後關閉網路連線
System.out.println(result);
RPC.stopProxy(proxy);
}
}
注意上面RPC獲取代理方法中介面是呼叫物件的介面物件,由此可以得出在客戶端呼叫的業務類的方法是定義在業務類的介面中的。該介面實現了 VersionedProtocal 介面。
完成了上面的操作,我們先啟動服務端,再啟動客戶端。觀察服務端和客戶端輸出資訊。然後,我們在命令列輸入之前檢視hadoop節點執行情況的命令jps,輸出如下圖:
我們可以看到一個 java 程序,是“MyServer”,該程序正是我們剛剛執行的 rpc 的服務端類MyServer。那麼可以判斷,hadoop 啟動時產生的 5 個 java 程序也應該是RPC 的服務端。我們觀察 NameNode 的原始碼,可以看到 NameNode 確實建立了RPC 的服務端(在namenode類的初始化Initialize方法中,為方便觀察,我複製原始碼,重點檢視create server部分)。
/**
* Initialize name-node.
*
* @param conf the configuration
*/
private void initialize(Configuration conf) throws IOException {
InetSocketAddress socAddr = NameNode.getAddress(conf);
UserGroupInformation.setConfiguration(conf);
SecurityUtil.login(conf, DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY,
DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY, socAddr.getHostName());
int handlerCount = conf.getInt("dfs.namenode.handler.count", 10);
// set service-level authorization security policy
if (serviceAuthEnabled =
conf.getBoolean(
ServiceAuthorizationManager.SERVICE_AUTHORIZATION_CONFIG, false)) {
PolicyProvider policyProvider =
(PolicyProvider)(ReflectionUtils.newInstance(
conf.getClass(PolicyProvider.POLICY_PROVIDER_CONFIG,
HDFSPolicyProvider.class, PolicyProvider.class),
conf));
ServiceAuthorizationManager.refresh(conf, policyProvider);
}
myMetrics = NameNodeInstrumentation.create(conf);
this.namesystem = new FSNamesystem(this, conf);
if (UserGroupInformation.isSecurityEnabled()) {
namesystem.activateSecretManager();
}
<span style="color:#FF0000;">// create rpc server</span>
InetSocketAddress dnSocketAddr = getServiceRpcServerAddress(conf);
if (dnSocketAddr != null) {
int serviceHandlerCount =
conf.getInt(DFSConfigKeys.DFS_NAMENODE_SERVICE_HANDLER_COUNT_KEY,
DFSConfigKeys.DFS_NAMENODE_SERVICE_HANDLER_COUNT_DEFAULT);
this.serviceRpcServer = <span style="color:#FF0000;">RPC.getServer(this, dnSocketAddr.getHostName(),
dnSocketAddr.getPort(), serviceHandlerCount,
false, conf, namesystem.getDelegationTokenSecretManager());</span>
this.serviceRPCAddress = this.serviceRpcServer.getListenerAddress();
setRpcServiceServerAddress(conf);
}
this.server = RPC.getServer(this, socAddr.getHostName(),
socAddr.getPort(), handlerCount, false, conf, namesystem
.getDelegationTokenSecretManager());
// The rpc-server port can be ephemeral... ensure we have the correct info
this.serverAddress = this.server.getListenerAddress();
FileSystem.setDefaultUri(conf, getUri(serverAddress));
LOG.info("Namenode up at: " + this.serverAddress);
startHttpServer(conf);
this.server.start(); //start RPC server
if (serviceRpcServer != null) {
serviceRpcServer.start();
}
startTrashEmptier(conf);
}
由上可以看到 NameNode 本身就是一個 java 程序。觀察圖 5-2 中 RPC.getServer()方法的第一個引數,發現是 this,說明 NameNode 本身就是一個位於服務端的被呼叫物件,即 NameNode 中的方法是可以被客戶端程式碼呼叫的。根據 RPC 執行原理可知,NameNode暴露給客戶端的方法是位於介面中的。繼續檢視namenode類的介面實現,可以看到 NameNode 實現了 ClientProtocal、DatanodeProtocal、NamenodeProtocal 等介面。
下面在接著分析namenode實現的這些常用介面作用,由誰呼叫實現:
ClientProtocal 由DFSclient呼叫
這個介面是供客戶端呼叫的。這裡的客戶端不是指的我們自己寫的程式碼,而是hadoop 的一個類叫做 DFSClient。在 DFSClient 中會呼叫 ClientProtocal 中的方法,完成一些操作。
該介面中的方法大部分是對 HDFS 的操作,如 create、delete、mkdirs、rename 等。
DatanodeProtocal 由DataNode呼叫
這個介面是供 DataNode 呼叫的。 DataNode 呼叫該介面中的方法向 NameNode 報告本節點的狀態和 block 資訊。
NameNode 不 能 向 DataNode 發 送 消 息 , 只 能 通 過 該 接 口 中 方 法 的 返 回 值 向DataNode 傳遞訊息。
NamenodeProtocal 由SecondaryNameNode 呼叫
這個介面是供 SecondaryNameNode 呼叫的。SecondaryNameNode 是專門做NameNode 中 edits 檔案向 fsimage 合併資料的。
對於datanode節點的介面實現,分析思路大體一致,就是根據具體介面,檢視介面協議功能,這是檢視hadoop原始碼的學習經驗,常用的一些myeclipse快捷鍵如下:
檢視一個基類或者介面的派生類或實現類---滑鼠指向類名,Ctrl + T ;
檢視函式的呼叫關係(找到所有呼叫該方法的函式)--Ctrl + Alt + H (ubuntu系統快捷鍵佔用,可以類名右鍵找open call Hierarchy,結果在控制檯輸出) ;
快速查詢類物件的相關資訊 -- Ctrl + O(查詢類名的所有成員變數和方法),F3檢視類名的定義;
具體快捷鍵可以通過滑鼠類名,右鍵檢視所有可用方法。
那對於datanode的介面大家可以通過上面快捷鍵練習下,也分析下介面功能,學習datanode重要兩個介面,分別是 InterDatanodeProtocal、ClientDatanodeProtocal。今天就總結到這裡。