Hadoop RPC详解-RMI

来源:互联网 发布:js过滤对象中字符串 编辑:程序博客网 时间:2024/04/30 17:09

Hadoop RPC 远程过程调用是Hadoop中的核心概念。在深入研究RPC之前,先看看远程调用的鼻祖Java RMI.


1.什么是RMI

Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。

Java RMI极大地依赖于接口。在需要创建一个远程对象的时候,程序员通过传递一个接口来隐藏底层的实现细节。客户端得到的远程对象句柄正好与本地的根代码连接,由后者负责透过网络通信。这样一来,程序员只需关心如何通过自己的接口句柄发送消息。

接口的两种常见实现方式是:

(1)最初使用JRMP(Java Remote Message Protocol,Java远程消息交换协议)实现;

(2)此外还可以用与CORBA兼容的方法实现。

RMI一般指的是编程接口,也有时候同时包括JRMP和API(应用程序编程接口),而RMI-IIOP则一般指RMI接口接管绝大部分的功能,以支持CORBA的实现。最初的RMI API设计为通用地支持不同形式的接口实现。后来,CORBA增加了传值(pass by value)功能,以实现RMI接口。然而RMI-IIOP和JRMP实现的接口并不完全一致。

2.RMI实例

RMI相关的类和接口都在jdk java.rmi包中。

RMI的基础是接口,RMI构架基于一个重要的原理:定义接口和定义接口的具体实现是分开的。

下面我们通过具体的例子,建立一个简单的远程计算服务和使用它的客户程序

一个正常工作的RMI系统由下面几个部分组成:
远程服务的接口定义

远程服务接口的具体实现

Stub 和 Skeleton 文件

一个运行远程服务的服务器

一个RMI命名服务,它允许客户端去发现这个远程服务

类文件的提供者(一个HTTP或者FTP服务器)

一个需要这个远程服务的客户端程序

下面我们一步一步建立一个简单的RMI系统。首先在你的机器里建立一个新的文件夹,以便放置我们创建的文件,为了简单起见,我们只使用一个文件夹存放客户端和服务端代码,并且在同一个目录下运行服务端和客户端。如果所有的RMI文件都已经设计好了,那么你需要下面的几个步骤去生成你的系统:

   1、   编写并且编译接口的Java代码

 2、   编写并且编译接口实现的Java代码

 3、   从接口实现类中生成 Stub 和 Skeleton 类文件

 4、   编写远程服务的主运行程序

 5、   编写RMI的客户端程序

 6、   安装并且运行RMI系统

2.1 接口

第一步就是建立和编译服务接口的Java代码。这个接口定义了所有的提供远程服务的功能,下面是源程序:

/** *  */package com.renren;import java.rmi.Remote;import java.rmi.RemoteException;/** * @author root *  */public interface CalculatorRemoteInterface extends Remote {//! 注意,这个接口继承自Remote,每一个定义的方法都必须抛出一个RemoteException异常对象public abstract int add(int a, int b) throws RemoteException;}

注意,这个接口继承自Remote,每一个定义的方法都必须抛出一个RemoteException异常对象

2.2 实现类

package com.renren;import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;@SuppressWarnings("serial")// 继承 UnicastRemoteObject 远程对象 这个一定要写 否则 服务器启动报异常public class CalculatorRemoteObject extends UnicastRemoteObject implements        CalculatorRemoteInterface {    public CalculatorRemoteObject() throws RemoteException {        super();        // TODO Auto-generated constructor stub    }    /**     *     */    private static final long serialVersionUID = 1L;    // @Override    public int add(int a, int b) {        // TODO Auto-generated method stub        return a + b;    }}

2.3 打包

将上述两个java文件编译打包为rmi-test.jar

在编写server和client程序时需要依赖该jar包

2.4 Server

Server做的事很简单,代码注释很详细...

package com.test;import java.net.MalformedURLException;import java.rmi.AlreadyBoundException;import java.rmi.Naming;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;public class Server {    public static void main(String[] args) {        // TODO Auto-generated method stub        try {            // 定义远程接口CalculatorRemoteInterface对象 用于绑定在服务器注册表上 该接口由CalculatorRemoteObject类实现            CalculatorRemoteInterface remoteObject = new CalculatorRemoteObject();            // 定义一个端口号            int port = 9999;            // 创建一个接受对特定端口调用的远程对象注册表 注册表上需要接口一个指定的端口号            LocateRegistry.createRegistry(port);            // 定义服务器远程地址URL格式            String serverAddress = "rmi://10.2.185.197:" + port+"/calculator" ;            // 绑定远程地址和接口对象            Naming.bind(serverAddress, remoteObject);            // 如果启动成功 则弹出如下信息            System.out.println(">>>服务器启动成功");            System.out.println(">>>请启动客户端进行连接访问");        } catch (MalformedURLException e) {            System.out.println("地址出现错误!");            e.printStackTrace();        } catch (AlreadyBoundException e) {            System.out.println("重复绑定了同一个远程对象!");            e.printStackTrace();        } catch (RemoteException e) {            System.out.println("创建远程对象出现错误!");            e.printStackTrace();        }    }    }

2.5 Client

package com.test;import java.net.MalformedURLException;import java.rmi.Naming;import java.rmi.NotBoundException;import java.rmi.RemoteException;public class Client {public static void main(String[] args) {// TODO Auto-generated method stubtry {CalculatorRemoteInterface calculator=(CalculatorRemoteInterface) Naming.lookup("rmi://10.2.185.197:9999/calculator");System.out.println("5+3="+calculator.add(5, 3));} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NotBoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

2.6 测试

1.在一台机器上启动Server

2.在两外一台机器上启动Client,可以看到Client成功调用了Server的CalculatorRemoteObject对象的add方法.

2.7 名词解释 Stub Skeleton

http://blog.sina.com.cn/s/blog_4ab0d57401009n4a.html

Stub(存根)和Skeleton(骨架)
  Stub和Skeleton是经过rmic命令生成的,我们的程序要通过远程调用,底层一定是套接字的字节传输,要一个对象序列化成为一个字节数组,传输到服务器或者客户端的对端之后,再把该对象反序列化成为对应的对象,这些网络传输的过程要求安全,稳定等等非常麻烦的操作,Stub驻留客户端,承担着代理远程对象的实现者的角色,Skeleton类帮助远程对象与Stub再RMI连接上进行通信。RMI的客户与Stub进行交换,Stub与Skeleton通信,Skeketon负责与服务器进行交互,因此有了Stub和Skeleton之后我们就不需要实现底层通信的细节,我们进行的远程调用,只需要通过接口对方法进行操作即可,使分布式调用实现了位置上的透明,即:远程调用就像本地调用一样。
   通俗的说,RMI的代理模式迫使方法调用必须通过充当替身的代理对象,即Stub和Skeleton,由这些代理对象将方法传递给实际的对象。
   在远程虚拟机中,每个远程对象都有一个Skeleton对应,Skeleton负责将调用分配给实际的远程对象来实现,他的工作是在服务器端的。在JDK1.2版本以上的环境中不需要Skeleton,因为Jdk1.2推出了附加的Skeleton,并且对底层的协议也进行了修改,性能上有很大提升,默认的rmic命令是不会产生Skeleton类的,如果要想生成Skeleton类可以使用rmic-v1.1 即可。
   Stub的操作任务:
    a、初始化与包含远程对象远程虚拟机的连接
    b、与远程虚拟机参数进行编组,也就是调度,包括参数的写入以及传输
    c、等待方法的调用结果
    d、解编返回值和返回的异常。也就是读取服务器上的返回值,也称为反调度
    e、将结果返回给调用程序。
   Skeleton对应的任务:
    a、读取远程方法的参数,也就是解编
    b、调用实际远程对象上的方法
    c、将结果或者异常组编(写入并传输)给调用程序。
   Stub与Skeleton的关系以及操作是对应的关系。只要我们有编译好的远程对象的类,就可以调用jdk的rmic命令来生成stub和skeleton了,关键是我们需要理解Stub是客户端应用程序的代理,Skeleton是服务器端应用程序的代理,他们之间协作完成客户端与服务器之间的方法调用时的通信。
   了解者两个概念之后,我们接下来需要了解JNDI以及RMI注册表两个概念,等剩下的两个概念了解了,我们基本上对RMI就可以入门了。
 

参考:

http://blog.csdn.net/wangxingbao4227/article/details/6842951

http://blog.csdn.net/tin591/article/details/8117198

http://hi.baidu.com/dl_linfeng/item/330036304422c65c80f1a778




0 0
原创粉丝点击