Hadoop的RPC主要是通过Java的动态代理(Dynamic Proxy)与反射(Reflect)实现,代理类是由java.lang.reflect.Proxy类在运行期时根据接口,采用Java反射功能动态生成的,并且结合java.lang.reflect.InvocationHandler来处理客户端的请求,当用户调用这个动态生成的实现类时,实际上是调用了InvocationHandler实现类的invoke方法。RPC源代码在org.apache.hadoop.ipc下,有以下几个主要类: 
    Client: 客户端,连接服务器、传递函数名和相应的参数、等待结果;
    Server:服务器端,主要接受Client的请求、执行相应的函数、返回结果;
    VersionedProtocol:通信双方所遵循契约的父接口;
    RPC:RPC通信机制,主要是为通信的服务方提供代理。

  1.通信双方遵循的契约

    要通过RPC服务进行通信,服务的提供方必须实现某个接口,而这个即可是VersionedProtocol的子类,诸如:
InterTrackerProtocol,它是TaskTracker与JobTracker进行通信所遵循的契约,JobTracker是一个Server,它必须实现这个接口;
JobSubmissionProtocol,它是JobTracker与JobClient通讯所遵循的契约,JobClient利用契约中的方法可以提交作业去执行, 并且得到当前系统的状态;
DatanodeProtocol,利用此契约,DataNode可以向NameNode汇报自己的块状态以及负载情况。
InterDatanodeProtocol,DataNode之间利用此契约可以更新数据块。
其它的接口在此不再一一赘述。

    2.Hadoop中RPC通信原理 

  我们通过TaskTracker与JobTracker的通信来剖析其通信过程,JobTracker的代理是通过下面的方法得到的,
 this.jobClient = (InterTrackerProtocol)
UserGroupInformation.getLoginUser().doAs(
new PrivilegedExceptionAction<Object>() {
public Object run() throws IOException {
return RPC.waitForProxy(InterTrackerProtocol.class,
InterTrackerProtocol.versionID,
jobTrackAddr, fConf);
}
});

  它是通过调用RPC类中的静态方法waitForProxy()方法而得到了InterTrackerProtocol的一个代理,借助于这个代理对象,TaskTracker就可以与JobTracker进行通信了。

  VersionedProtocol proxy =
(VersionedProtocol) Proxy.newProxyInstance(
protocol.getClassLoader(), new Class[] { protocol },
new Invoker(protocol, addr, ticket, conf, factory, rpcTimeout));

  跟踪Hadoop的源代码,我们可以发现PRC.waitForProxy()最终是调用的Proxy.newProxyInstance()来创建一个代理对象,第一个参数是类加载器(代理类在运行的过程中动态生成),第二个参数是要实现的代理类的接口,第三个参数是InvokercationHandler接口的子类,最终调用的也就是InvokercationHandler实现类的的invoker()方法。

  private static class Invoker implements InvocationHandler {
private Client.ConnectionId remoteId;
private Client client;
..... public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
final boolean logDebug = LOG.isDebugEnabled();
long startTime = 0;
if (logDebug) {
startTime = System.currentTimeMillis();
} ObjectWritable value = (ObjectWritable)
client.call(new Invocation(method, args), remoteId);
if (logDebug) {
long callTime = System.currentTimeMillis() - startTime;
LOG.debug("Call: " + method.getName() + " " + callTime);
}
return value.get();
} ....
}

  我们可以看到,InvocationHandler的实现类Invoker中主要包含两个成员变量即remoteId(唯一标识RPC的服务器端)、Client(通过工厂模式得到的客户端),invoke()方法中最重要的就是下面的语句:

ObjectWritable value = (ObjectWritable)client.call(new Invocation(method, args), remoteId);

  其中call方法的第一个参数封装调用方法和参数并实现Writable接口的对象,以便于在分布式环境中传输,第二个参数勿需多言,它就用于唯一标识RPC Server,也就是与指定的Server进行通信。call方法的核心代码如下:

  public Writable call(Writable param, ConnectionId remoteId)  throws InterruptedException, IOException {
Call call = new Call(param);
Connection connection = getConnection(remoteId, call);//请看下面的说明
connection.sendParam(call); // 将参数封装成一个call对象发送给Server
boolean interrupted = false;
synchronized (call) {
while (!call.done) {
try {
call.wait(); // 等待Server发送的内容
} catch (InterruptedException ie) {
// save the fact that we were interrupted
interrupted = true;
}
}
...
return call.value;
}

  其中竟然出现了一个Call对象,我们看到此方法返回的结果是call对象的一个成员变量,也就是说Call封装了Client的请求以及Server的响应,synchronized的使用会同步Client的请求以及Server的响应。通Connection对象的sendParam方法可以将请求发送给Server,那么Connection又是什么呢?

   private Connection getConnection(ConnectionId remoteId,Call call) throws IOException, InterruptedException {
do {
synchronized (connections) {
connection = connections.get(remoteId);
if (connection == null) {
connection = new Connection(remoteId);
connections.put(remoteId, connection);
}
}
} while (!connection.addCall(call)); ...
connection.setupIOstreams();
return connection;
}

  其实Connection是扩展Thread而得到的一个线程,最终把所有的connection对象都放入到一个Hashtable中,同一个ConnectionId的Connection可以复用,降低了创建线程的开销。connection.setupIOstreams()用于在真正的建立连接,并将RPC的header写入到输出流中,通过start方法启动线程,其核心代码如下所示:

  public void run() {
while (waitForWork()) {//等到可以读响应时返回true
receiveResponse();
}   

  receiveResponse方法主要是从输入流反序列化出value,并将其封装在call对象中,这样client端就得到了server的响应,核心代码如下:

private void receiveResponse() {
try {
int id = in.readInt(); // 读取连接id,以便从calls中取出相应的call对象
Call call = calls.get(id);
int state = in.readInt(); // 读取输入流的状态
if (state == Status.SUCCESS.state) {
Writable value = ReflectionUtils.newInstance(valueClass, conf);
value.readFields(in); // read value
call.setValue(value);
calls.remove(id);
}
...
}

才疏学浅,错误之处在所难免,恳请各位予以指正。。

Hadoop之RPC的更多相关文章

  1. Hadoop的RPC分析

    一.基础知识 原理 http://www.cnblogs.com/edisonchou/p/4285817.html,这个谢了一些rpc与hadoop的例子. 用到了java的动态代理,服务端实现一个 ...

  2. Hadoop的RPC框架介绍

    为什么会引入RPC: RPC采用客户机/服务器模式.请求程序就是一个客户机,而服务提供程序就是一个服务器.当我们讨论HDFS的,通信可能发生在: Client-NameNode之间,其中NameNod ...

  3. Hadoop之RPC简单使用(远程过程调用协议)

    一.RPC概述 RPC是指远程过程调用,也就是说两台不同的服务器(不受操作系统限制),一个应用部署在Linux-A上,一个应用部署在Windows-B或Linux-B上,若A想要调用B上的某个方法me ...

  4. Hadoop的RPC通信原理

    RPC调用: RPC(remote procedure call)远程过程调用: 不同java进程间的对象方法的调用. 一方称作服务端(server),一方称为客户端(client): server端 ...

  5. Hadoop的RPC机制及简单实现

    1.RPC简介 Remote Procedure Call 远程过程调用协议 RPC——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.RPC协议假定某些 ...

  6. Hadoop的RPC工作原理

    RPC远程过程调用: Hadoop的远程过程调用(Remote Procedure Call,RPC)是Hadoop中核心通信机制,RPC主要通过所有Hadoop的组件元数据交换,如MapReduce ...

  7. hadoop的RPC机制 -源码分析

    这些天一直奔波于长沙和武汉之间,忙着腾讯的笔试.面试,以至于对hadoop RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上 ...

  8. 【Hadoop】Hadoop 中 RPC框架原理、代码示例

    0.内容 1.hadoop中的RPC框架封装思想 2.Hadoop RPC 实现方法 3.服务调用动态转发和负载均衡的实现思考 4.协议代码: package com.ares.hadoop.rpc; ...

  9. Hadoop中RPC协议小例子报错java.lang.reflect.UndeclaredThrowableException解决方法

    最近在学习传智播客吴超老师的Hadoop视频,里面他在讲解RPC通信原理的过程中给了一个RPC的小例子,但是自己编写的过程中遇到一个小错误,整理如下: log4j:WARN No appenders ...

随机推荐

  1. php学习-快速开发框架thinkphp-day1

    以下操作针对windows系统. 1.下载thinkphp3.23-all并解压 官方网站: http://www.thinkphp.cn/down.html 2.开发工具使用phpstorm htt ...

  2. Agile.Net 组件式开发平台 - 数据报表组件

    Agile.Report.dll 文件为平台数据报表支持库,基于FasstReport.Net扩展重写,提供了非常强大的自定义报表的功能使开发者为应用程序快速有效地生成报表.报表类库提供了创建报表所需 ...

  3. html dom的加载

    操作HTML DOM文档的一个难题是,你的JavaScript代码可能在DOM完全载入之前运行,这会导致你的代码产生一些问题.页面加载时浏览器内部操作的顺序大致是这样的: 1. HTML被解析. 2. ...

  4. sql中的触发器、视图、事务

    ·触发器(trigger) [触发器本质上还是一个存储过程,只不过不是用exe来调用执行,而是通过增删改数据库的操作] [触发器只对增.删.改有效] 触发器的格式 (instead of与for的区别 ...

  5. oracle开启audit(审计)

    1.查看审计功能是否开启(本机已经开启,如果audit_sys_operations值为FALSE就是没开审计) [sql] view plaincopyprint? SQL> CONN /AS ...

  6. nodejs包管理

    包管理 :npm cnpm yarn Node Package Manager node的包管理工具 cls清除控制台 npm install jquery 下载依赖包 就自动下载最新版本的jq np ...

  7. 开启Windows的索引服务

    除开SearchEverything电脑内部的全硬盘搜索之外,如果要搜索文件内的内容的话,就无能为力的了.Window内置的索引服务就派上用场了,这么好的服务,默认设置居然是关闭的,想不通. 下面来介 ...

  8. css和jQuery ,字符串中重新定义单个字符样式背景

    <table class="tb2"> <tr><td class="td1">融资登记企业<span>985& ...

  9. 一个汉字的ASCII编码&#12288;

    一个汉字的ASCII编码:  

  10. C#快速学习笔记(译)续一

    6.虚拟和非虚拟函数 下面是一个非虚拟函数 using System; namespace Test2 { class Plane { public double TopSpeed() {return ...