`
zqhxuyuan
  • 浏览: 31532 次
  • 性别: Icon_minigender_1
  • 来自: 福建
社区版块
存档分类
最新评论

Hadoop源码分析-RPC

阅读更多

方法

说明

waitForProxy

保证namenode启动正常且连接正常,主要由SecondayNode、Datanode、JobTracker使用

stopProxy

停止代理

getProxy

创建代理实例,获得代理实例的versioncode,再与getProxy()传入的versioncode做对比, 相同返回代理,不同抛出VersionMismatch异常

getServer

创建并返回一个Server实例,由TaskTracker、JobTracker、NameNode、DataNode使用

call

向一系列服务器发送一系列请求,在源码中没见到那个类使用该方法。但注释提到了:Expert,应该是给系统管理员使用的接口

 

内部类

说明

ClientCache

缓存Client对象

Invocation

用于封装方法名和参数,作为数据传输层。每次RPC调用传的参数实体类,其中Invocation包括了调用方法和配置文件

Invoker

具体的调用类,采用动态代理机制,继承InvocationHandler,

有remoteId和client成员,id用以标识异步请求对象,client用以调用实现代码

Server

Server的具体类,实现了抽象类的call方法,获得传入参数的call实例,再获取method方法,使用反射机制调用具体的方法

VersionMismatch

版本不匹配异常,三个参数interfaceName, clientVersion, serverVersion

 

Invocation类仅作为VO,ClientCache类只是作为缓存,而Server类用于服务端的处理,他们都和客户端的数据流和业务逻辑没有关系。重点是Invoker类

Invoker使用了Java的动态代理:

Dynamic Proxy是由两个class实现的:java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler, 后者是一个接口。

动态代理类是指在运行时生成的class, 在生成它时你必须提供一组interface给它, 然后该class就宣称它实现了这些interface,即接口的代理。

Dynamic Proxy是典型的Proxy模式, 它不会替你作实质性的工作, 在生成它的实例时你必须提供一个handler(InvocationHandler), 由它接管实际的工作。

这个handler, 在Hadoop的RPC中, 就是Invoker对象,Invoker实现了InvocationHandler接口。

可以简单地理解:通过一个接口来生成一个类, 这个类上的所有方法调用, 都会传递到你生成类时传递的InvocationHandler实现中。

 

在Hadoop的RPC中, Invoker实现了InvocationHandler的invoke方法(invoke方法也是InvocationHandler的唯一方法)。

Invoker会把所有跟这次调用相关的调用方法名, 参数类型列表, 参数列表打包, 然后利用Client, 通过socket传递到服务器端。

就是说, 在proxy类上的任何调用, 都通过Client发送到远方的服务器上。

 

Invoker使用Invocation。Invocation封装了一个远程调用的所有相关信息, 它的主要属性有: 

methodName, 调用方法名, parameterClasses, 调用方法参数的类型列表和parameters, 调用方法参数。注意, 它实现了Writable接口, 可以串行化。

 

RPC.Server实现了org.apache.hadoop.ipc.Server, 你可以把一个对象, 通过RPC, 升级成为一个服务器。

服务器接收到请求,接收到的是Invocation对象, 反序列化后, 得到方法名, 方法参数列表和参数列表。

利用Java反射, 我们就可以调用对应的对象的方法。

调用的结果再通过socket, 返回给客户端, 客户端把结果解包后, 就可以返回给Dynamic Proxy的使用者了。

 

接口协议

把某些接口和接口中的方法称为协议,客户端和服务端只要实现这些接口中的方法就可以进行通信了 

Hadoop的RPC机制正是采用了这种“架构层次的协议”,有一整套作为协议的接口

/**
 * Superclass of all protocols that use Hadoop RPC. 所有RPC协议接口的父接口
 */
public interface VersionedProtocol {
  /**
   * Return protocol version corresponding to protocol interface. 返回对应的协议接口的协议版本
   * @param protocol The classname of the protocol interface 协议接口的类名
   * @param clientVersion The version of the protocol that the client speaks 客户端版本
   * @return the version that the server will speak 服务器版本
   */
  public long getProtocolVersion(String protocol, long clientVersion) throws IOException;
}

实现VersionedProtocol接口的接口

 

HDFS相关

协议接口

 

ClientDatanodeProtocol

client与datanode交互的接口,操作不多,只有一个block恢复的方法。

那么,其它数据请求的方法呢?client与datanode主要交互是通过流式的socket实现,源码在DataXceiver

ClientProtocol

client与Namenode交互的接口,所有控制流的请求均在这里,如:创建文件、删除文件等

DatanodeProtocol

Datanode与Namenode交互的接口,如心跳、blockreport等

NamenodeProtocol

SecondaryNode与Namenode交互的接口

Mapreduce相关

协议接口

 

InterDatanodeProtocol

Datanode内部交互的接口,用来更新block的元数据

InnerTrackerProtocol

TaskTracker与JobTracker交互的接口,功能与DatanodeProtocol相似

JobSubmissionProtocol

JobClient与JobTracker交互的接口,用来提交Job、获得Job等与Job相关的操作

TaskUmbilicalProtocol

Task中子进程与母进程交互的接口,子进程即map reduce等操作,母进程即TaskTracker,该接口会汇报子进程的运行状态

其它

协议接口

 

AdminOperationProtocol

不用用户操作的接口,提供一些管理操作,如刷新JobTracker的node列表

RefreshAuthorizationPolicyProtocol

 

RefreshUserMappingsProtocol 

 

 

Invocation

  /** A method invocation, including the method name and its parameters.*/
  private static class Invocation implements Writable, Configurable { //实现hadoop的序列化接口Writable,因为要在Client和Server之间传输该对象
    private String methodName;  // The name of the method invoked. 
    private Class[] parameterClasses;  // The parameter classes. 
    private Object[] parameters;  // The parameter instances. 
    private Configuration conf;

    public Invocation() {}
    public Invocation(Method method, Object[] parameters) {
      this.methodName = method.getName();
      this.parameterClasses = method.getParameterTypes();
      this.parameters = parameters;
  }
  // 序列化
    public void readFields(DataInput in) throws IOException {
      methodName = UTF8.readString(in);
      parameters = new Object[in.readInt()];
      parameterClasses = new Class[parameters.length];
      ObjectWritable objectWritable = new ObjectWritable();
      for (int i = 0; i < parameters.length; i++) { //数组类型,每个数组元素也都需要序列化
        parameters[i] = ObjectWritable.readObject(in, objectWritable, this.conf);
        parameterClasses[i] = objectWritable.getDeclaredClass();
      }
    }
  // 反序列化
    public void write(DataOutput out) throws IOException {
      UTF8.writeString(out, methodName);
      out.writeInt(parameterClasses.length);
      for (int i = 0; i < parameterClasses.length; i++) {
        ObjectWritable.writeObject(out, parameters[i], parameterClasses[i], conf);
      }
  } 
  }

ClientCache

  /* Cache a client using its socket factory as the hash key */
  static private class ClientCache {
    private Map<SocketFactory, Client> clients = new HashMap<SocketFactory, Client>();

    /**
     * Construct & cache an IPC client with the user-provided SocketFactory if no cached client exists.
     * @param conf Configuration
     * @return an IPC client
     */
    private synchronized Client getClient(Configuration conf, SocketFactory factory) {
      // Construct & cache client.  The configuration is only used for timeout, and Clients have connection pools.
      // So we can either (a) lose some connection pooling and leak sockets,
      // or (b) use the same timeout for all configurations.
      // Since the IPC is usually intended globally, not per-job, we choose (a).
      Client client = clients.get(factory);
      if (client == null) {
        client = new Client(ObjectWritable.class, conf, factory);
        clients.put(factory, client);
      } else {
        client.incCount();
      }
      return client;
    }
    /**
     * Construct & cache an IPC client with the default SocketFactory if no cached client exists.
     */
    private synchronized Client getClient(Configuration conf) {
      return getClient(conf, SocketFactory.getDefault());
    }

    /**
     * Stop a RPC client connection 
     * A RPC client is closed only when its reference count becomes zero.
     */
    private void stopClient(Client client) {
      synchronized (this) {
        client.decCount();
        if (client.isZeroReference()) {
          clients.remove(client.getSocketFactory());
        }
      }
      if (client.isZeroReference()) {
        client.stop();
      }
    }
  }
  private static ClientCache CLIENTS=new ClientCache();

  static Client getClient(Configuration conf) { //for unit testing only
    return CLIENTS.getClient(conf);
  }

Invoker

  private static class Invoker implements InvocationHandler {
    private Client.ConnectionId remoteId;
    private Client client;
    private boolean isClosed = false;

    private Invoker(Class<? extends VersionedProtocol> protocol, InetSocketAddress address, UserGroupInformation ticket,
        Configuration conf, SocketFactory factory, int rpcTimeout, RetryPolicy connectionRetryPolicy) throws IOException {
      this.remoteId = Client.ConnectionId.getConnectionId(address, protocol, ticket, rpcTimeout, connectionRetryPolicy, conf);
      this.client = CLIENTS.getClient(conf, factory);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      ObjectWritable value = (ObjectWritable) client.call(new Invocation(method, args), remoteId);
      return value.get();
    }
    
    /* close the IPC client that's responsible for this invoker's RPCs */ 
    synchronized private void close() {
      if (!isClosed) {
        isClosed = true;
        CLIENTS.stopClient(client);
      }
    }
  }

 一般我们看到的动态代理的invoke()方法中总会有 method.invoke(ac, arg); 而上面invoke() 中却没有,这是为什么? 其实使用 method.invoke(ac, arg); 是在本地JVM中调用;而在hadoop中,是将数据发送给服务端,服务端将处理的结果再返回给客户端,所以这里的invoke()方法必然需要进行网络通信.要让服务端能知道客户端想要调用的是哪个接口,接口和其他参数比如要调用的地址等封装为remoteId, 这是Client的内部类ConnectionId,唯一确定一个连接

 

waitForProxy()

  static VersionedProtocol waitForProxy(Class<? extends VersionedProtocol> protocol,
	  long clientVersion, InetSocketAddress addr, Configuration conf, int rpcTimeout, long connTimeout) throws IOException { 
    long startTime = System.currentTimeMillis();
    IOException ioe;
    while (true) {
      try {
        return getProxy(protocol, clientVersion, addr, conf, rpcTimeout);
      } catch(ConnectException se) {  // namenode has not been started
        LOG.info("Server at " + addr + " not available yet, Zzzzz...");
        ioe = se;
      } catch(SocketTimeoutException te) {  // namenode is busy
        LOG.info("Problem connecting to server: " + addr);
        ioe = te;
      }
      // check if timed out
      if (System.currentTimeMillis()-connTimeout >= startTime) {
        throw ioe;
      }
      // wait for retry
      try {
        Thread.sleep(1000);
      } catch (InterruptedException ie) {} // IGNORE
    }
  }

getProxy()

  /** Construct a client-side proxy object that implements the named protocol,talking to a server at the named address. */
  public static VersionedProtocol getProxy(Class<? extends VersionedProtocol> protocol, long clientVersion, InetSocketAddress addr, 
 	  UserGroupInformation ticket, Configuration conf, SocketFactory factory, int rpcTimeout, RetryPolicy connectionRetryPolicy) throws IOException {
    if (UserGroupInformation.isSecurityEnabled()) {
      SaslRpcServer.init(conf);
    }
    final Invoker invoker = new Invoker(protocol, addr, ticket, conf, factory, rpcTimeout, connectionRetryPolicy);
    VersionedProtocol proxy = (VersionedProtocol)Proxy.newProxyInstance(protocol.getClassLoader(), new Class[]{protocol}, invoker);
    long serverVersion = proxy.getProtocolVersion(protocol.getName(), clientVersion);
    if (serverVersion == clientVersion) {
      return proxy;
    } else {
      throw new VersionMismatch(protocol.getName(), clientVersion, serverVersion);
    }
  }

和05中的RPC类的getProxy()方法相似。

getProxy()会生成协议接口VersionedProtocol的代理对象,当客户端调用接口的方法,会回调Invoker对象的invoke方法

Invoker实现了Java的InvocationHandler接口,和例子一样,客户端会发送封装好的Invocation对象给服务端。

Invocation对象封装了客户端想要调用的服务端的接口,方法,参数。

服务端会调用具体的接口的方法,并返回方法的执行结果,类型为ObjectWritable

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      ObjectWritable value = (ObjectWritable) client.call(new Invocation(method, args), remoteId);
      return value.get();
    }

 

下一节分析client.call()是怎么将Invocation对象从客户端想服务端发送。

在分析Client之前,要明确以下几点目标:

客户端和服务端的连接是怎样建立的?
2. 客户端是怎样给服务端发送数据的?
3. 客户端是怎样获取服务端的返回数据的

 

现在来总结下Hadoop的RPC和我们自己实现的RPC的映射关系

角色

作用

05例子的对应类

Client

RPC服务的客户端

Client

RPC

实现了一个简单的RPC模型

RPC

Server

服务端的抽象类

Server接口

RPC.Server

服务端的具体类

RPC.RPCServer

VersionedProtocol

所有使用RPC服务的类都要实现该接口,在创建代理时用来判断代理对象是否创建正确

Echo接口

Invoker

动态代理

InvocationHandler

Invocation (RPC)  

Call (Client/Server)

封装客户端要调用的接口,方法,参数;以及服务端返回的方法执行结果

Invocation

Connection(Client)

Listener(Server)

处理远程连接对象: 监听客户端写入; 转发给服务端调用具体方法; 向客户端写回数据

Listener

 

RPC中关于服务端的操作: RPC.Server内部类, call(), getServer() 在后面分析RPC.Server时一起分析

  • 大小: 42.4 KB
  • 大小: 25.7 KB
分享到:
评论

相关推荐

    Hadoop源码分析- RPC DataNode

    NULL 博文链接:https://zqhxuyuan.iteye.com/blog/1879292

    Hadoop RPC机制分析

    NULL 博文链接:https://wmwork2010.iteye.com/blog/632016

    Hadoop 培训课程(2)HDFS

    Hadoop 培训课程(2)HDFS 分布式文件系统与HDFS HDFS体系结构与基本概念*** HDFS的shell操作*** java接口及常用api*** ---------------------------加深拓展---------------------- ...HDFS的分布式存储架构的源码分析**

    hadoop段海涛老师八天实战视频

    10-hdfs下载数据源码分析-getFileSystem2.avi 第三天 mapreduce的原理和编程 01-hdfs源码跟踪之打开输入流.avi 02-hdfs源码跟踪之打开输入流总结.avi 03-mapreduce介绍及wordcount.avi 04-wordcount的编写和...

    新版Hadoop视频教程 段海涛老师Hadoop八天完全攻克Hadoop视频教程 Hadoop开发

    10-hdfs下载数据源码分析-getFileSystem2.avi 第三天 mapreduce的原理和编程 01-hdfs源码跟踪之打开输入流.avi 02-hdfs源码跟踪之打开输入流总结.avi 03-mapreduce介绍及wordcount.avi 04-wordcount的编写和...

    [HBase]源码级强力分析hadoop的RPC机制

    这些天一直奔波于长沙和武汉之间,忙着腾讯的笔试、面试,以至于对hadoopRPC(RemoteProcedureCallProtocol,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。...

    妳那伊抹微笑_云计算之Hadoop完美笔记2.0

    NameNode源码分析(RPC是基础) DataNode源码分析 FileSystem源码分析(如何与NameNode通信ClientProtocol) JobTracker源码分析 TaskTracker源码分析 网站日志分析项目(这个项目分析可以让你更加掌握好所学的知识...

    Apache Storm(apache-storm-2.3.0-src.tar.gz 源码)

    Apache Storm 有很多用例:实时分析、在线机器学习、连续计算、分布式 RPC、ETL 等等。Apache Storm 速度很快:基准测试显示每个节点每秒处理超过一百万个元组。它具有可扩展性、容错性,保证您的数据将得到处理,...

    Apache Storm(apache-storm-2.3.0-src.zip 源码)

    Apache Storm 有很多用例:实时分析、在线机器学习、连续计算、分布式 RPC、ETL 等等。Apache Storm 速度很快:基准测试显示每个节点每秒处理超过一百万个元组。它具有可扩展性、容错性,保证您的数据将得到处理,...

    MapReduceV1:TaskTracker端启动Task流程分析

    我们基于Hadoop1.2.1源码分析MapReduceV1的处理流程。TaskTracker周期性地向JobTracker发送心跳报告,在RPC调用返回结果后,解析结果得到JobTracker下发的运行Task的指令,即LaunchTaskAction,就会在TaskTracker...

    CAT实时应用监控平台-其他

    CAT作为服务端项目基础组件,提供了Java,C/C++,Node.js,Python,Go等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团...

Global site tag (gtag.js) - Google Analytics