RPC,即Remote Procdure Call,中文名:远程过程调用;
(1)它允许一台计算机程序远程调用另外一台计算机的子程序,而不用去关心底层的网络通信细节,对我们来说是透明的。因此,它经常用于分布式网络通信中。
RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
(2)Hadoop的进程间交互都是通过RPC来进行的,比如Namenode与Datanode直接,Jobtracker与Tasktracker之间等。
因此,可以说:Hadoop的运行就是建立在RPC基础之上的。
(1)透明性:远程调用其他机器上的程序,对用户来说就像是调用本地方法一样;
(2)高性能:RPC Server能够并发处理多个来自Client的请求;
(3)可控性:jdk中已经提供了一个RPC框架—RMI,但是该PRC框架过于重量级并且可控之处比较少,所以Hadoop RPC实现了自定义的PRC框架。
(1)RPC采用了C/S的模式;
(2)Client端发送一个带有参数的请求信息到Server;
(3)Server接收到这个请求以后,根据发送过来的参数调用相应的程序,然后把自己计算好的结果发送给Client端;
(4)Client端接收到结果后继续运行;
同其他RPC框架一样,Hadoop RPC分为四个部分:
(1)序列化层:Clent与Server端通信传递的信息采用了Hadoop里提供的序列化类或自定义的Writable类型;
(2)函数调用层:Hadoop RPC通过动态代理以及java反射实现函数调用;
(3)网络传输层:Hadoop RPC采用了基于TCP/IP的socket机制;
(4)服务器端框架层:RPC Server利用java NIO以及采用了事件驱动的I/O模型,提高RPC Server的并发处理能力;
Hadoop RPC在整个Hadoop中应用非常广泛,Client、DataNode、NameNode之间的通讯全靠它了。例如:我们平时操作HDFS的时候,使用的是FileSystem类,它的内部有个DFSClient对象,这个对象负责与NameNode打交道。在运行时,DFSClient在本地创建一个NameNode的代理,然后就操作这个代理,这个代理就会通过网络,远程调用到NameNode的方法,也能返回值。
(1)动态代理
About:动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实,代理对象对客户隐藏了实际对象。目前Java开发包中提供了对动态代理的支持,但现在只支持对接口的实现。
(2)反射——动态加载类
(3)序列化
(4)非阻塞的异步IO(NIO)
Java NIO原理请参考阅读:http://weixiaolu.iteye.com/blog/1479656
Hadoop RPC对外主要提供了两种接口(见类org.apache.hadoop.ipc.RPC),分别是:
(1)public static <T> ProtocolProxy <T> getProxy/waitForProxy(…)
构造一个客户端代理对象(该对象实现了某个协议),用于向服务器发送RPC请求。
(2)public static Server RPC.Builder (Configuration).build()
为某个协议(实际上是Java接口)实例构造一个服务器对象,用于处理客户端发送的请求。
(1)定义RPC协议
RPC协议是客户端和服务器端之间的通信接口,它定义了服务器端对外提供的服务接口。
(2)实现RPC协议
Hadoop RPC协议通常是一个Java接口,用户需要实现该接口。
(3)构造和启动RPC SERVER
直接使用静态类Builder构造一个RPC Server,并调用函数start()启动该Server。
(4)构造RPC Client并发送请求
使用静态方法getProxy构造客户端代理对象,直接通过代理对象调用远程端的方法。
如下所示,我们定义一个IProxyProtocol 通信接口,声明了一个Add()方法。
public interface IProxyProtocol extends VersionedProtocol {
static final long VERSION = 23234L; //版本号,默认情况下,不同版本号的RPC Client和Server之间不能相互通信
int Add(int number1,int number2);
}
需要注意的是:
(1)Hadoop中所有自定义RPC接口都需要继承VersionedProtocol接口,它描述了协议的版本信息。
(2)默认情况下,不同版本号的RPC Client和Server之间不能相互通信,因此客户端和服务端通过版本号标识。
Hadoop RPC协议通常是一个Java接口,用户需要实现该接口。对IProxyProtocol接口进行简单的实现如下所示:
public class MyProxy implements IProxyProtocol {
public int Add(int number1,int number2) {
System.out.println("我被调用了!");
int result = number1+number2;
return result;
}
public long getProtocolVersion(String protocol, long clientVersion)
throws IOException {
System.out.println("MyProxy.ProtocolVersion=" + IProxyProtocol.VERSION);
// 注意:这里返回的版本号与客户端提供的版本号需保持一致
return IProxyProtocol.VERSION;
}
}
这里实现的Add方法很简单,就是一个加法操作。为了查看效果,这里通过控制台输出一句:“我被调用了!”
这里通过RPC的静态方法getServer来获得Server对象,如下代码所示:
public class MyServer {
public static int PORT = 5432;
public static String IPAddress = "127.0.0.1";
public static void main(String[] args) throws Exception {
MyProxy proxy = new MyProxy();
final Server server = RPC.getServer(proxy, IPAddress, PORT, new Configuration());
server.start();
}
}
这段代码的核心在于第5行的RPC.getServer方法,该方法有四个参数,第一个参数是被调用的java对象,第二个参数是服务器的地址,第三个参数是服务器的端口 。获得服务器对象后,启动服务器。这样,服务器就在指定端口监听客户端的请求。到此为止,服务器就处于监听状态,不停地等待客户端请求到达。
这里使用静态方法getProxy或waitForProxy构造客户端代理对象,直接通过代理对象调用远程端的方法,具体如下所示:
public class MyClient {
public static void main(String[] args) {
InetSocketAddress inetSocketAddress = new InetSocketAddress(
MyServer.IPAddress, MyServer.PORT);
try {
// 注意:这里传入的版本号需要与代理保持一致
IProxyProtocol proxy = (IProxyProtocol) RPC.waitForProxy(
IProxyProtocol.class, IProxyProtocol.VERSION, inetSocketAddress,
new Configuration());
int result = proxy.Add(10, 25);
System.out.println("10+25=" + result);
RPC.stopProxy(proxy);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
以上代码中核心在于RPC.waitForProxy(),该方法有四个参数,第一个参数是被调用的接口类,第二个是客户端版本号,第三个是服务端地址。返回的代理对象,就是服务端对象的代理,内部就是使用java.lang.Proxy实现的。
经过以上四步,我们便利用Hadoop RPC搭建了一个非常高效的客户机–服务器网络模型。
(1)启动服务端,开始监听客户端请求
(2)启动客户端,开始向服务端发请求
(3)查看服务端状态,是否被调用
SUMMARY:从上面的RPC调用中,可以看出:在客户端调用的业务类的方法是定义在业务类的接口中的。该接口实现了VersionedProtocal接口。
(4)现在我们在命令行执行jps命令,查看输出信息,会出现如下图所示的:
从上图中可以看到一个java进程,是“MyServer”,该进程正是我们刚刚运行的RPC的服务端类MyServer。因此,大家可以联想到我们搭建Hadoop环境时,也执行过该命令用来判断Hadoop的相关进程是否全部启动。
SUMMARY:那么可以判断,Hadoop启动时产生的5个java进程也应该是RPC的服务端。
下面我们观察NameNode的源代码,如下图所示,可以看到NameNode确实创建了RPC的服务端。
private void initialize(Configuration conf) throws IOException {
......
// create rpc server
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 = RPC.getServer(this, dnSocketAddr.getHostName(),
dnSocketAddr.getPort(), serviceHandlerCount,
false, conf, namesystem.getDelegationTokenSecretManager());
this.serviceRPCAddress = this.serviceRpcServer.getListenerAddress();
setRpcServiceServerAddress(conf);
}
this.server = RPC.getServer(this, socAddr.getHostName(),
socAddr.getPort(), handlerCount, false, conf, namesystem
.getDelegationTokenSecretManager());
......
}
(1)thomas0yang,《Hadoop RPC框架》:http://blog.csdn.net/thomas0yang/article/details/41211259
(2)姜维,《Hadoop RPC机制分析》:http://blog.csdn.net/jiangwei0910410003/article/details/21155911 (此文从源码角度分析了RPC,想要深入了解的可以阅读此文)
(3)吴超,《Hadoop的底层架构—RPC机制》:http://www.superwu.cn/2013/08/05/360
(4)东苑草根,《Hadoop RPC基础》:http://www.cnblogs.com/dycg/p/rpc.html
(5)Suddenly,《Hadoop日记Day10-RPC机制》:http://www.cnblogs.com/sunddenly/p/3983193.html
(6)董西成,《Hadoop RPC使用方法》:http://book.51cto.com/art/201312/422043.htm
作者:周旭龙
出处:http://edisonchou.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。