`

深入了解javaRMI

阅读更多

 前言:JavaRMI的使用并不麻烦,让我真正感兴趣的是stubs和skeletons,更让我感兴趣的是RMI框架如何让客户端感觉像在调用本地方法一样去使用远程对象的方法。(阅读本文建议先了解rmi的基本用法,可参考附件demo)

本篇文章重点关注以下问题:

  • 什么是stubs和skeletons对象?谁创建了他们?扮演什么角色?
  • 客户端如何找到服务端?如何知道服务端发布服务的端口?
  • 注册中心是否是必须的?什么作用?

1、回归本质需求

        首先忘记注册中心,最开始的使用场景是这样的:服务端Server发布服务,客户端Client需要使用到Server发布的服务(可以理解成api),Server继承了java.rmi.server.UnicastRemoteObject,问题在于Server和Client运行在不同的机器上面,而Client却想执行一个在远程机器上Server发布的方法。

        很容易想到,Rmi想解决上述问题肯定会涉及到Socket网络编程,因为Server运行在远程机器上,牵扯到具体实现有两个关键点:

  • 客户端Client如何从处理网络连接中解耦开来,从而专注于业务?
  • 客户端Client如何像调用本地方法一样来调用远程机器上的方法?

        上述问题就是stubs和skeletons存在的意义,正是stubs和skeletons的存在才能让客户端和服务器不需要处理网络相关的方法。stubs同样实现了和服务端同样的java.rmi.server.UnicastRemoteObject接口,这样当Client想调用Server上的方法时,就可以调用stubs上的相同的方法,但是stub上只有和网络相关的处理逻辑,并没有对应的业务处理逻辑。比如说Server上发布了一个add方法,则stub中也同样有一个add方法,但是stub上的这个add方法并不包含业务逻辑部分的实现,它仅仅包含如何连接到远程的skeleton,调用方法的详细信息、参数、返回值等。整个实现架构如下图所示:

 

         简言之,就是客户端Client和stub对话,stub和skeleton对话,skeleton和Server对话,Server执行真正的方法,饭后把结果原路返回。由图中也可以看出,Rmi的功能主要由四个部分组成:Client、stub、skeleton、Server。

2、Socket层详细信息

socket层通信的整个流程:

1.Server在远程机器上发布服务,并监听一个端口。这个端口是JVM或是OS在运行时随机选择的一个端口,可以说Server在远程机器的端口上发布服务。

2.Client调用Server上发布的方法。其实Client并不知道Server在哪,也并不知道Server在监听哪个端口,但是Client有stub,stub知道所有这些东西,这样Client就可以调用stub上他想调用的任何方法;

3、stub链接到Server监听的端口上并发送参数,过程如下:

       > Client连接到Server监听的端口;

       > Server收到请求并创建一个socket来处理这个链接;

       > Server继续监听到来的请求;

       > Client和Server使用双发商量的协议来传送参数和结果;(协议可以是JRMP或者 iiop)

4. 方法在远程Server上执行,并将执行结果返回给stub

5. stub返回结果给Client,就好像stub执行了这个方法一样。

 

但是,仔细揣摩上述过程,似乎仍有漏洞,第二点说stub知道Server在哪,stub知道Server监听的端口,但是Server发布服务的端口是随机的,stub如何知道Server?不知道Server的Ip和Port也就不可能创建一个知道一切的stub。这也引出RMIRegistry注册中心的存在价值。

3、RMIRegistry注册中心的作用

         RmiRegistry可以看做是一个服务,它维护了一个hashMap,key为publicName,value为stunObject。比如远程机器上布置一个服务Calculator,并随机在某个端口上发布(此端口客户端无法得知),Server发布服务后会同时在服务端创建一个stub对象,然后把它注册到RmiRegistry,这样注册中心RmiRegistry就知晓了Server和Client之间网络传输的所有条件,当前,前提是Client可以联系到RmiRegistry。庆幸的是,RMIRegistry 发布服务的默认端口是公开的、广为人知的,即1099,当然,也可以自己指定注册服务的端口,这样Client就可以从注册中心RMIRegistry 得到这个stub对象。

4、结合例子阐述整个过程

4.1 服务端发布服务

    定义远程服务接口:

 

/**
 * 定义一个远程接口,必须继承Remote接口,其中需要远程调用的方法必须抛出RemoteException异常 
 * @author Administrator
 */
public interface IHello extends Remote {
    /** 
     * 简单的返回“Hello World!"字样 
     * @return 返回“Hello World!"字样 
     * @throws java.rmi.RemoteException 
     */ 
    public String helloWorld() throws RemoteException; 

    /** 
     * 一个简单的业务方法,根据传入的人名返回相应的问候语 
     * @param someBodyName  人名 
     * @return 返回相应的问候语 
     * @throws java.rmi.RemoteException 
     */ 
    public String sayHelloToSomeBody(String someBodyName) throws RemoteException; 
}

    Server服务端的实现:

 

/**
 * 远程的接口的实现 
 * @author Administrator
 */
public class HelloImpl extends UnicastRemoteObject implements IHello {
    private static final long serialVersionUID = -5638936712154214504L;

    /**
     * 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须声明抛出RemoteException异常 
     * @throws RemoteException
     */
    public HelloImpl() throws RemoteException {} 
    
    @Override
    public String helloWorld() throws RemoteException {
        return "Hello World!";
    }

    @Override
    public String sayHelloToSomeBody(String someBodyName) throws RemoteException {
        return "你好," + someBodyName + "!"; 
    }

}

    Server发布服务,大致 步骤如下:

  1. 创建一个远程服务对象,及实现;
  2. 在指定的端口上发布RmiRegistry服务,用户客户端连接;
  3. 在注册中心上发布服务;
public static void main(String[] args) {
        try { 
            // 1. 创建一个远程对象 
            IHello rhello = new HelloImpl(); 
            // 2. 本地主机上的远程对象注册表Registry的实例,并指定端口为8888,这一步必不可少,缺少注册表创建,则无法绑定对象到远程注册表上 
            LocateRegistry.createRegistry(8888); 

            // 3. 把远程对象注册到RMI注册服务器上,并命名为RHello 
            //绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的) 
            //Naming.bind("rmi://localhost:8888/RHello",rhello); 
            Naming.bind("//localhost:8888/RHello",rhello); 

            System.out.println(">>>>>INFO:远程IHello对象绑定成功!"); 
        } catch (RemoteException e) { 
            System.out.println("创建远程对象发生异常!"); 
            e.printStackTrace(); 
        } catch (AlreadyBoundException e) { 
            System.out.println("发生重复绑定对象异常!"); 
            e.printStackTrace(); 
        } catch (MalformedURLException e) { 
            System.out.println("发生URL畸形异常!"); 
            e.printStackTrace(); 
        } 

    上述为服务端Server发布服务的整个过程。

 4.2 客户端使用服务端发布的服务

    客户端上定义一个同远程接口上的服务接口(包名应该与服务器发布的接口包名一致)

    

/**
 * (包名应该与服务器发布的接口包名一致)
 * @author Administrator
 */
public interface IHello extends Serializable {
    /** 
     * 简单的返回“Hello World!"字样 
     * @return 返回“Hello World!"字样 
     * @throws java.rmi.RemoteException 
     */ 
    public String helloWorld() throws RemoteException; 

    /** 
     * 一个简单的业务方法,根据传入的人名返回相应的问候语 
     * @param someBodyName  人名 
     * @return 返回相应的问候语 
     * @throws java.rmi.RemoteException 
     */ 
    public String sayHelloToSomeBody(String someBodyName) throws RemoteException; 
}
   客户端上使用服务端发布的服务,步骤如下:

 

 

  1. 向注册中心查找stun;
  2. 使用服务。
/**
 * 客户端测试,在客户端调用远程对象上的远程方法,并返回结果。 
 * @author Administrator
 */
public class HelloClient {

    public static void main(String[] args) {
        try { 
            // 1. 在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法 
            IHello rhello =(IHello) Naming.lookup("rmi://localhost:8888/RHello"); 
            // 2. 调用服务
            System.out.println(rhello.helloWorld()); 
            System.out.println(rhello.sayHelloToSomeBody("熔岩")); 
        } catch (NotBoundException e) { 
            e.printStackTrace(); 
        } catch (MalformedURLException e) { 
            e.printStackTrace(); 
        } catch (RemoteException e) { 
            e.printStackTrace();   
        } 
    }

}

 

补充说明:之前在多网卡ubantu系统上调试RMI,发现服务端发布绑定的URI并不是我所指定的。如果服务端有多个网卡,它只是使用其中任意一个网卡,所以在多网卡的服务器上开启RMI服务的话得指定所使用的IP。常规办法如下:

  • 如果是CS程序,在启动RMI服务前,通过以下代码指定RMI服务使用的IP地址:

                    System.setProperty("java.rmi.server.hostname","192.168.1.111");

  • 如果是WEB程序,添加如下启动参数:

                    -Djava.rmi.server.hostname=192.168.1.111

    以Tomcat为例(Windows)

    如果用startup.bat方式启动,则在catalina.bat文件中加入如下语句:

    set CATALINA_OPTS=-Djava.rmi.server.hostname=192.168.1.111

 项目百度网盘下载地址:http://pan.baidu.com/s/1eRQE0aY,密码:4mvn
  • 大小: 15.4 KB
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    java RMI技术实现的网络聊天室

    理解套接字编程的基本原理有助于深入理解RMI的工作机制。 以上是关于“java RMI技术实现的网络聊天室”的主要知识点。通过这个项目,开发者可以学习到如何使用RMI创建分布式应用,以及如何处理并发和网络通信等问题...

    Java RMI实验

    理解并完成这个实验,有助于深入理解Java RMI的工作原理,包括对象的序列化、远程对象的导出和注册、以及客户端如何通过RMI调用远程方法等核心概念。同时,这也是实现分布式应用的一个基础步骤,对于构建大规模、高...

    java RMI实现代码

    通过这个“java RMI实现代码”项目,你可以深入理解RMI的使用方式,包括如何创建远程接口、实现远程对象,以及客户端和服务器端的交互过程。实践中,结合代码注释逐步调试,将有助于更好地掌握这一核心技术。

    深入了解javaRMI-附件资源

    深入了解javaRMI-附件资源

    JAVA RMI简单例子

    Java RMI(Remote Method Invocation,远程方法...虽然现代Java开发中,RMI可能不是首选的分布式计算解决方案(例如,Web服务和RESTful API更为流行),但了解RMI的基本工作原理对于理解Java的分布式特性仍然很有价值。

    Java RMI中文规范

    Java RMI(Remote Method Invocation,远程方法调用)是Java平台中用于构建分布式对象系统的关键技术。它允许Java应用程序在不同Java...通过深入理解RMI的原理和特性,开发者可以构建出高效、可靠的分布式解决方案。

    JAVA RMI测试代码

    Java RMI(Remote Method Invocation,远程方法调用)是Java平台提供的一种分布式计算技术,它允许Java对象在不同的网络环境中进行交互,就...通过分析这些代码,可以深入理解RMI的工作原理以及如何在实际项目中应用。

    Java RMI 可运行实例

    首先,理解Java RMI的基本概念至关重要。RMI的核心是使Java对象能够在网络上的不同虚拟机(JVM)之间进行通信。这种通信基于Java对象的序列化和反序列化,使得远程对象的行为能够像本地对象一样被调用。 1. **RMI...

    java_rmi.rar_RMI java_java.rmi

    学习和理解Java RMI对于开发分布式Java应用至关重要,它可以帮助你构建可扩展、高性能的应用程序,尤其适用于服务器集群和云环境。通过实践提供的"rmi"代码示例,你可以深入理解RMI的工作原理,并掌握如何在实际项目...

    关于java RMI分布式程序开发实例

    这个实例将带你深入理解Java RMI的原理和实际应用。 一、RMI基本概念 1. 远程接口:远程接口定义了可以在远程对象上调用的方法。这些接口需要继承`java.rmi.Remote`接口,并声明可能会抛出`java.rmi....

    java RMI分布式计算(两矩阵相乘)源码

    Java RMI,全称为Remote Method Invocation,是Java平台上的一个特性,用于实现远程对象调用。...通过分析这些代码,我们可以深入理解Java RMI在分布式计算中的应用,以及如何利用它来解决实际问题。

    java rmi HelloWorld版(源码)

    这个"java rmi HelloWorld版(源码)"的压缩包文件提供了一个简单的RMI应用示例,帮助开发者了解和学习RMI的基本原理和使用。 RMI的核心概念包括: 1. **远程接口(Remote Interface)**:这是定义远程方法的接口...

    JAVA RMI.rar_Java RMI_ME_RMI java_rmi

    Java Remote Method Invocation (RMI) 是Java平台中用于构建分布式应用程序的一种重要技术。RMI允许Java对象在不同的Java虚拟机(JVM)之间调用...通过阅读"read me"文件,可以更深入地理解这个项目是如何实现RMI的。

    通过Java RMI实现远程调用的一个简单例子

    Java RMI(Remote Method ...通过查看这些源代码,你可以更深入地了解RMI的工作原理。同时,参考提供的博文链接(https://forchase.iteye.com/blog/1454428)可以获取更多关于这个示例的详细解释和上下文信息。

    rmi.zip_Java RMI_java rmi网络_java源代码 RMI

    学习和理解Java RMI,你需要掌握如何创建远程接口和实现,如何启动RMI服务器,如何在客户端进行远程对象的查找和调用,以及如何处理可能出现的网络异常。此外,对于序列化和网络通信的基础知识也是必不可少的。通过...

    JavaRMI分布式编程心得

    ### Java RMI 分布式编程心得详解 ...通过深入理解 RMI 的工作原理、架构设计以及实战经验,开发者可以更好地利用这项技术解决实际问题。希望本文能够为正在探索或已经涉足 Java RMI 领域的读者带来有用的启示。

    RMI.rar_Java RMI_java.rmi_java.rmi.Remot_remote

    Java RMI(远程方法调用)是Java编程语言中的一项核心技术,自JDK 1.1版本起就被引入...虽然现代的Java框架如Spring、EJB等提供了更高级别的分布式服务,但理解RMI的基本原理对于深入学习Java分布式系统仍然是必要的。

    Java RMI实例

    Java Remote Method Invocation (RMI) 是Java平台提供的一种分布式计算技术,它允许Java对象在不同的JVM(Java ...通过学习和运行这个实例,开发者能够深入理解Java RMI的工作机制,并将其应用到更复杂的分布式系统中。

    Rmi.rar_Java RMI_RMI java_java RMI 线程_rmi

    这个教程“Rmi.rar”显然包含了关于如何使用Java RMI实现不同方式的实例,这些实例可能是精心设计和测试的,旨在帮助学习者深入理解RMI的工作原理。 RMI的核心概念包括: 1. **远程接口(Remote Interface)**:...

    rmi.rar_Java RMI_RMI source code_java RMI simple_rmi

    Java Remote Method Invocation (RMI) 是Java平台提供的一种强大的分布式计算技术,允许在不同网络...通过深入理解并实践这些示例,你将能够更好地掌握Java RMI的工作原理和使用技巧,为构建分布式系统打下坚实的基础。

Global site tag (gtag.js) - Google Analytics