Java RMI 使用

1. Java RMI

Java RMI(Java Remote Method Invocation),即Java远程方法调用,是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它的实现依赖于Java虚拟机(JVM),因此它仅支持从一个JVM到另一个JVM的调用。这些JVM可以在不同的主机上、也可以在同一个主机上。

RMI 使用 JRMP(Java Remote Message Protocol,Java远程消息交换协议)实现,本质是TCP网络通信,内部封装了序列化和通信过程,通过代理使得客户端运行的程序可以调用远程服务器上的对象。JRMP是实现RPC(Remote Procedure Call,远程过程调用)的一种方式。远程调用可以用于一个进程调用另一个进程(很可能在另一个远程主机上)中的过程,从而提供了过程的分布能力。Java 的 RMI 则在 RPC 的基础上向前又迈进了一步,即提供分布式对象间的通讯。

Java RMI 由三个部分构成:

  1. rmiregistry(JDK提供的一个可以独立运行的程序,在bin目录下);
  2. server端的程序,对外提供远程对象;
  3. client端的程序,想要调用远程对象的方法。

2. 使用步骤

  1. 先启动rmiregistry服务,启动时可以指定服务监听的端口,也可以使用默认的端口(1099)。
  2. server端在本地先实例化一个提供服务的实现类,然后通过RMI提供的Naming/Context/Registry等类的bind或rebind方法将刚才实例化好的实现类注册到rmiregistry上并对外暴露一个名称。
  3. client端通过本地的接口和一个已知的名称(即rmiregistry暴露出的名称),再使用RMI提供的Naming/Context/Registry等类的lookup方法从RMIService那拿到实现类。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,便可以实现远程调用对象的方法了。

RMI步骤
rmi时序图

3. 存根和骨干网通信过程

方法调用从客户对象经存根(Stub)、远程引用层(Remote Reference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传输层,向上穿过远程引用层和骨干网(Skeleton),到达服务器对象。

存根:扮演着远程服务器对象的代理的角色,使该对象可被客户激活。
远程引用层:处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。
传输层:管理实际的连接,并且追踪可以接受方法调用的远程对象。
骨干网:完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传输层和远程调用层返回。最后,占位程序获得返回值。

存根和骨干网的具体通信

4. 使用 Naming 实现 RMI

UserService 继承 Remote 接口,标识可以从非本地虚拟机上调用的接口,成为存在于服务器端的远程对象,任何远程对象都必须直接或间接实现此接口。供客户端访问并提供一定的服务,接口中方法要声明抛出了RemoteException异常,对象需要序列化。

public interface UserService extends Remote {
    String getName() throws RemoteException;
    void setName(String name) throws RemoteException;
    Integer getAge() throws RemoteException;
    void setAge(Integer age) throws RemoteException;
    void sayHello() throws RemoteException;
    void sayHello(String name) throws RemoteException;
    void sayHello(String name, Integer age) throws RemoteException;
    void getHashCode() throws RemoteException;
}

远程对象必须继承 UniCastRemoteObject 类,保证客户端访问获得远程对象时,该远程对象将会把自身的一个拷贝以 Socket 的形式传输给客户端。

此时客户端所获得的这个拷贝称为“存根”(stub),而服务器端本身已存在的远程对象则称之为“骨架”(skeleton)。

此时的存根是客户端的一个代理,用于与服务器端的通信,而骨架也可认为是服务器端的一个代理,用于接收客户端的请求之后调用远程方法来响应客户端的请求。

public class User extends UnicastRemoteObject implements UserService {
    private String name;
    private Integer age;

    // 构造函数中将生成stub和skeleton
    public User() throws RemoteException{
        super();
    }

    @Override
    public String getName() throws RemoteException {
        return name;
    }

    @Override
    public void setName(String name) throws RemoteException {
        this.name = name;
    }

    @Override
    public Integer getAge() throws RemoteException {
        return age;
    }

    @Override
    public void setAge(Integer age) throws RemoteException {
        this.age = age;
    }

    @Override
    public void sayHello() throws RemoteException {
        System.out.println("sayHello() hello " + name + " " + age);
    }

    @Override
    public void sayHello(String name) throws RemoteException {
        System.out.println("sayHello(String name) hello " + name);
    }

    @Override
    public void sayHello(String name, Integer age) throws RemoteException {
        System.out.println("sayHello(String name, Integer age) hello " + name + " " + age);
    }

    @Override
    public void getHashCode() throws RemoteException {
        System.out.println("hashCode " + this.hashCode());
    }
}

注册远程对象,向客户端提供远程对象服务。远程对象是在远程服务上创建的,将远程对象注册到RMI Service之后,客户端就可以通过RMI Service请求到该远程服务对象的stub了,利用stub代理就可以访问远程服务对象了。

public class UserRmiServer {
    public static void main(String[] args) throws Exception {
        User user = new User();
        // 创建本地主机上的远程对象注册表Registry的实例,默认端口1099
        LocateRegistry.createRegistry(10001);
        Naming.bind("rmi://127.0.0.1:10001/rmiUser", user);
    }
}

从 Registry 中检索远程对象的存根(stub),通过stub调用远程接口实现

public class UserRmiClient {
    public static void main(String[] args) throws Exception {  
        UserService userService = (UserService) Naming.lookup("rmi://127.0.0.1:10001/rmiUser");

        userService.sayHello();
        userService.setName("张三");
        userService.setAge(20);
        System.out.println("name=" + userService.getName() + ";age=" + userService.getAge());

        userService.sayHello();
        userService.sayHello("李四");
        userService.sayHello("王五", 30);
        userService.getHashCode();
    }
}

测试
rmiserver
rmiclient

5. 优缺点

优点
给分布计算的系统设计、编程都带来了极大的方便。只要按照RMI规则设计程序,可以不必再过问在RMI之下的网络细节了,如:TCP和Socket等等。任意两台计算机之间的通讯完全由RMI负责。调用远程计算机上的对象就像本地对象一样方便。

缺点
RMI对服务器的IP地址和端口依赖很紧密。RMI是Java语言的远程调用,两端的程序语言必须是Java实现,Scala和Java可以互通。

参考:
Java文档 RMI教程
理解Java RMI 一篇就够
分布式架构基础:Java RMI详解
JAVA RMI 原理和使用浅析
java RMI原理详解
从懵逼到恍然大悟之Java中RMI的使用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值