Java的RMI远程方法调用实现和应用

最近在学习Dubbo,RMI是很重要的底层机制,RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制。使用这种机制,某一台计算机(即JVM虚拟机)上的对象可以调用另外一台计算机上的对象来获取远程数据。

RMI的实现对建立分布式Java应用程序至关重要,是Java体系非常重要的底层技术。

>>RMI的概念和原理

RMI思路是在客户端安装一个代理(proxy),代理是位于客户端虚拟机中的一个对象,对于客户端对象,看起来就像访问的远程对象一样,客户端代理会使用网络协议与服务器进行通信。
同样的服务端也会有一个代理对象来进行通信的繁琐工作。

在RMI中,客户端的代理对象被称为存根(Stub),存根位于客户端机器上,它知道如何通过网络与服务器联系。存根会将远程方法所需的参数打包成一组字节。对参数编码的过程被称为参数编组(parameter marshalling),参数编组的目的是将参数转换成适合在虚拟机之间进行传递的形式。在RMI协议中,对象时使用序列化机制进行编码的。

总的来说,客户端的存根方法构造了一个信息块,它由以下几部分组成
被使用的远程对象的标识符;
被调用的方法的描述;
编组后的参数;

然后,存根将此信息发送给服务器。在服务器的一端,接收器对象执行以下动作:
定位要调用的远程对象;
调用所需的方法,并传递客户端提供的参数;
捕获返回值或调用产生的异常;
将返回值编组,打包送回给客户端存根;
客户端存根对来自服务器端的返回值或异常进行反编组,其结果就成为了调用存根返回值。

>>RMI体系结构

桩/框架(Stub/Skeleton)层:客户端的桩和服务器端的框架;
远程引用(remote reference)层:处理远程引用行为
传送层(transport):连接的建立和管理,以及远程对象的跟踪

 

>>RMI与代理模式

RMI的实现是典型的代理模式思想。

(1)代理模式的UML图

代理模式为其他对象提供一种代理以控制对这个对象的访问,把调用者与被调用者分离开,由代理负责传递信息来完成调用。
比如你想买美版Iphone6s,朋友出国,帮你海淘带回,整个过程就是代理模式,朋友作为代理,代你完成你想进行的操作。

代理模式能将代理对象与真正被调用的对象分离,在一定程度上降低了系统的耦合度;
在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用,代理对象也可以对目标对象调用之前进行其他操作。
代理模式在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢;同时也增加了系统的复杂度。

(2)代理模式在RMI这种的体现

远程代理的内部机制是这样的:

下面的RMI实例,其实就是一个代理模式的应用。

>>RMI的简单实例

RMI的开发步骤如下:
先创建远程接口及声明远程方法,注意这是实现双方通讯的接口,需要继承Remote
开发一个类来实现远程接口及远程方法,值得注意的是实现类需要继承UnicastRemoteObject
通过javac命令编译文件,通过java -server 命令注册服务,启动远程对象
最后客户端查找远程对象,并调用远程方法

(1)服务端远程接口
创建远程接口SearchService,接口必须继承Remote类,每一个定义方法都要抛出RemoteException异常


1

2

3

4

5

6

import java.rmi.Remote;

import java.rmi.RemoteException;

 

public interface SearchService extends Remote {

    public User findUser(String id) throws RemoteException;;

}

  

(2)建立SearchServiceImpl实现远程接口,注意此为远程对象实现类,需要继承UnicastRemoteObject


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

import java.rmi.RemoteException;

public class SearchServiceImpl implements SearchService{

 

    /**

     * 抛出RemoteException

     * @throws RemoteException

     */

    public SearchServiceImpl() throws RemoteException {

        super();

        }

     

    @Override

    public User findUser(String id) throws RemoteException {

        /**

         * 模拟查找返回数据

         */

        User user=new User(id,"Tom","18岁");

        return user;

    }

 

}

  

(3)为服务建立一个Model层,此对象需要远程传输,所以必须实现implements Serializable序列化接口,也就是可以在client和server端进行传输的可序列化对象
新建User类,用于数据传输:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

public class User implements Serializable{

 

    private static final long serialVersionUID = 1L;

     

    private String id;

    private String name;

    private String age;

     

    public User(String id,String name,String age){

        this.id=id;

        this.name=name;

        this.age=age;

    }

     

    public String getId() {

        return id;

    }

 

    public void setId(String id) {

        this.id = id;

    }

 

    public String getName() {

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    public String getAge() {

        return age;

    }

 

    public void setAge(String age) {

        this.age = age;

    }

     

    public String toString(){

        StringBuffer sb=new StringBuffer();

        sb.append("~用户id-"+id);

        sb.append("~用户姓名-"+id);

        sb.append("~用户年龄-"+id);

        return sb.toString();

    }

 

}

  

(4)建立服务器端,在服务器端注册RMI通讯端口与通讯路径


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

import java.net.MalformedURLException;

import java.rmi.Naming;

import java.rmi.RemoteException;

import java.rmi.registry.LocateRegistry;

import java.rmi.registry.Registry;

 

/**

 * @author BingYue

 */

public class Process {

     

    public static void main(String[] args){

         

        try {

            SearchService searchService=new SearchServiceImpl();

            //注册通信端口

            Registry registry=LocateRegistry.createRegistry(5678);

            //注册通讯路径

            Naming.rebind("rmi://127.0.0.1:5678/searchService", searchService);

            System.out.println("Service Start!");

             

        catch (RemoteException e) {

            e.printStackTrace();

        catch (MalformedURLException e) {

            e.printStackTrace();

        }

    }

 

}

  

(5)创建客户端,导入上面的实体类和接口,通过Naming.lookup()的方式调用
如果使用IDE,就可以新建一个项目,Client代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

import java.rmi.Naming;

 

/**

 * @author BingYue

 */

public class Client {

     

    public static void main(String[] args){

        try {

            //调用远程对象,注意RMI路径与接口必须与服务器配置一致

            SearchService searchService=(SearchService)Naming.lookup("rmi://127.0.0.1:5678/searchService");

            User user=searchService.findUser("100");

            System.out.println(user.toString());

        catch (Exception e) {

            e.printStackTrace();

        }

    }

}

  

(6)分别运行服务端和客户端,获得远程调用结果

>>RMI应用程序的基本模型

(1)Remote接口:是一个不定义方法的标记接口


1

Public interface Remote{}

在RMI中,远程接口声明了可以从远程Java虚拟机中调用的方法集。远程接口满足下列要求:
远程接口必须直接或间接扩展Java.rmi.Remote接口,且必须声明为public,除非客户端于远程接口在同一包中;
在远程接口中的方法在声明时,除了要抛出与应用程序有关的一场之外,还必须包括RemoteException(或它的超类,IOExcepion或Exception)异常;
在远程方法声明中,作为参数或返回值声明的远程对象必须声明为远程接口,而非该接口的实现类。

(2)RemoteObject抽象类实现了Remote接口和序列化Serializable接口,它和它的子类提供RMI服务器函数。

(3)LocateRegistry final()类用于获得特定主机的引导远程对象注册服务器程序的引用(即创建stub),或者创建能在特定端口接收调用的远程对象注册服务程序。

服务器端:向其他客户机提供远程对象服务


1

2

3

4

5

6

7

8

9

10

11

SomeService servcie=……;//远程对象服务

Registry registry=LocateRegisty.getRegistry();//Registry是个接口,他继承了Remote,此方法返回本地主机在默认注册表端口 1099 上对远程对象 Registry 的引用。

getRegistry(int port) 返回本地主机在指定 port 上对远程对象 Registry 的引用;

getRegistry(String host) 返回指定 host 在默认注册表端口 1099 上对远程对象 Registry 的引用;

getRegistry(String host, int port) 返回指定的 host 和 port 上对远程对象 Registry 的引用

 

registry.bind(“I serve”,service);// bind(String name,Remote obj) 绑定对此注册表中指定 name 的远程引用。name : 与该远程引用相关的名称 obj : 对远程对象(通常是一个 stub)的引用

unbind(String name)移除注册表中指定name的绑定。

rebind(String name,Remote obj)重新绑定,如果name已存在,但是Remote不一样则替换,如果Remote一样则丢弃现有的绑定

lookup(String name) 返回注册表中绑定到指定 name 的远程引用,返回Remote

String[] list() 返回在此注册表中绑定的名称的数组。该数组将包含一个此注册表中调用此方法时绑定的名称快照。

  

客户机端:向服务器提供相应的服务请求


1

2

3

Registry registry=LocateRegisty.getRegistry();

SomeService servcie=(SomeService)registry.lookup(“I serve”);

Servcie.requestService();

(4) Naming类和Registry类类似

客户端:


1

2

Naming.lookup(String url)

url 格式如下"rmi://localhost/"+远程对象引用

服务器端:


1

2

Registry registry=LocateRegistry.createRegistry(int port);

Naming.rebind(“service”,service);

(5) RMISecurityManager类

在RMI引用程序中,如果没有设置安全管理器,则只能从本地类路径加载stub和类,这可以确保应用程序不受由远程方法调用所下载的代码侵害
在从远程主机下载代码之前必须执行以下代码来安装RMISecurityManager:


1

System.setSecurityManager(new RMISecurityManager());

  

>>RMI的主要应用

RMI的用途是为分布式Java应用程序之间的远程通信提供服务,提供分布式服务。
目前主要应用时封装在各个J2EE项目框架中,例如Spring封装了RMI技术。

(1)在Spring中实现RMI

在服务器端定义服务的接口,定义特定的类实现这些接口;
在服务器端使用org.springframework.remoting.rmi.RmiServiceExporter类来注册服务;
在客户端使用org.springframework.remoting.rmi.RmiProxyFactoryBean来实现远程服务的代理功能;
在客户端定义访问与服务器端服务接口相同的类

(2)在Dubbo中的应用

Dubbo支持不同的协议,其中RMI协议底层就是Java RMI的实现,
RMI协议的Invoker转为Exporter发生在RmiProtocol类的export方法,
通过Spring或Dubbo或JDK来实现RMI服务,通讯细节由JDK底层来实现,节省了不少工作量。

时间: 2025-01-21 19:59:15

Java的RMI远程方法调用实现和应用的相关文章

Java RMI(远程方法调用) 实例与分析 (转)

目的: 通过本文,可以加深对Java RMI的理解,知道它的工作原理,怎么使用等. 也为了加深我自己的理解,故整理成文.不足之处,还望指出. 概念解释: RMI(RemoteMethodInvocation):远程方法调用,顾名思义,通过远程的方式调用非本地对象的方法并返回结果.使用远程调用通常解决本地计算瓶颈问题,例如分布式记算,最近很火的阿尔法狗人机大战,据说运算使用上千个CPU. JRMP(java remote method protocol):java远程方法协议,这是完成java到j

RMI远程方法调用讲解教程

RMI远程方法调用讲解教程 1.RMI概述 RMI(Remote Method Invocation) RMI是分布式对象软件包,它简化了在多台计算机上的JAVA应用之间的通信. 必须在jdk1.1以上 RMI用到的类 java.rmi.Remote 所有可以被远程调用的对象都必须实现该接口 java.rmi.server.UnicastRemoteObject 所有可以被远程调用的对象都必须扩展该类 什么是RMI 远程方法调用是一种计算机之间对象互相调用对方函数,启动对方进程的一 种机制,使用

Java中RMI远程调用

Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序可以调用远程服务器上的对象.远程方法调用特性使Java编程人员能够在网络环境中分布操作.RMI全部的宗旨就是尽可能简化远程接口对象的使用. Java RMI极大地依赖于接口.在需要创建一个远程对象的时候,程序员通过传递一个接口来隐藏底层的实现细节.客户端得到的远程对象句柄正好与本地的根代码连接,由后者负责透过

java使用rmi传输大文件示例分享_java

为什么要用RMI​在这次的项目中,对于客户端与服务器之间的通信,想了许多办法,由于做的是富客户端应用,最终将技术选定在了RMI和Java-sockets两种之间,其中RMI的灵活性不高,客户端和服务器端都必须是java编写,但使用比较方便,反观java-sockets,虽然比较灵活,但需要自己规定服务器端和客户端之间的通信协议.比较麻烦,几经权衡,最终还是选择RMI来进行服务器-客户端通信 文件上传问题在使用java-rmi的过程中,必然会遇到一个文件上传的问题,由于在rmi中无法传输文件流(比

java RMI原理详解

[本文转载自java RMI原理详解] 定义 RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法. 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中. Java RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序

RMI和socket详解

一般来说,基于CS(client-server)软件架构的开发技术有很多种.比较常用的有:基于socket的网络编程.RPC.基于Java技术的RMI(当然C#也有类似技术).CORBA等.在这里我们只是对基于socket的网络编程与RMI作个对比,有助于我们了解它们各自的应用领域,帮助我们在面对一个具体问题的时候选用适合的技术.另外,本文所做的讨论可以认为是脱离了语言层面的东西,只是对技术的本身做一个讨论,无关乎你是用C++.C#或Java 在开发.一.RMI技术简介        本文就以J

java web-javaweb servlet访问另外一台机器

问题描述 javaweb servlet访问另外一台机器 servlet访问另外一台机器,获得另外一台机器返回的一个值.这方面没接触过,说下思路,有代码更好了. 解决方案 假设你的Servlet是用来实现HTTP请求的,C为客户端,S1是你的servlet,S2是所谓的另一台机器中的Servlet.现在C向S1发起了一个HTTP请求1,而S1在处理这个HTTP请求1的时候,发现需要向S2发送HTTP请求2获取某些数据,因此它就向S2发送HTTP请求2.S2处理完HTTP请求2向S1返回结果,S1

Java RTTI and Reflection

Reference: Java编程思想 java 反射(Reflect) Java系列笔记(2) - Java RTTI和反射机制 Java Reflection in Action, 有空再补 ---------------------------------------------------------------------------------------------- 对于面向对象, 以多态的方式编写程序是首选的方式, 即使用基类reference, 而不是具体的类对象来编写程序

java工程之间相互传值

问题描述 java工程之间相互传值 a项目向b项目发送用户名和密码,操作b项目数据库,返回到a项目前台验证.问题是:两个项目之间怎么相互之间操作???(最好有源码) 解决方案 1.如果两个项目部署在同一个JVM中,可以直接a项目依赖b项目,然后直接调用b项目中的方法: 2.如果项目不部署在同一个JVM中,可以使用java中的RMI远程方法调用 解决方案二: 把B项目搞成基于Webservice的,提供一个接口出来,a项目直接调用,这样比较方便,也容易维护...