RMI(Remote Method Invocation,远程方法调用)是Java的一组拥护开发分布式应用程序的API。RMI使用Java语言接口定义了远程对象,它集合了Java序 列化和Java远程方法协议(Java Remote Method Protocol)。它大大增强了Java开发分布式应用的能力。简单地说,这样使原先的程序在同一操作系统的方法调用,变成了不同操作系统之间程序的方 法调用。这也就说明,RMI可以实现在不同的JVM之间执行方法的调用。由于J2EE是分布式程序平台,RMI机制实现程序组件在不同操作系统之间的通 信。

下图是RMI的体系结构:

通过RMI的体系结构,已经可以看出RMI的大致工作原理:

服务器端提供服务,服务中要暴露可以调用的远程方法,以接口的形式表现,这样在客户端可以通过服务接口来调用远程方法,实现复杂的业务逻辑。在服
务器端,首先要对接口中提供的方法实现,以便客户端调用能够完成一定的业务逻辑;接着需要生成Skeleton,在Skeleton中真正地实现了对商业
方法的调用,完成了客户请求的调用的过程,将获取到的调用方法的结果通过序列化机制返回给客户端,进行应答。在客户端,通过Stub来接收服务器返回的数
据(对象),即在这里进行了反序列化,也就是读取网络传输的字节流,进而进行重构。在Skeleton和Stub中,都对网络通信进行了处理,例如建立套
接字,建立网络连接,为实际的业务需要做好准备。

可见,客户端并没有真正地执行与服务器端组件进行直接交互。

这里,有个重要的概念,就是Java对象序列化机制。序列化,就是将对象写入流,以便能够在网络上传输对象,它是输出端执行的。反序列化,也就是
在接收端,为了能够获取传输的对象,需要将对象传输而来的字节流进行重构,重新得到完整的该对象。Java对象序列化的过程,是对已有的类的实例进行序列
化,首先要存在一个具体的实例。

下面通过在客户端调用服务器端的方法,实现一个例子,可以很直观地模拟RMI工作,从而进一步深化对RMI工作机制的理解。我使用Oracle JDeveloper 10g开发测试。

开发过程

1、在服务接口中,将客户端可以进行调用的方法暴露给客户端。

ShirdrnService接口中列出了可以调用的方法,ShirdrnService接口如下所示:

  1. package org.shirdrn.rmi.server;
  2. import java.io.IOException;
  3. public interface ShirdrnService {
  4. public String getServerTime() throws IOException, ClassNotFoundException;
  5. public String getGreetings () throws IOException, ClassNotFoundException;
  6. }

2、在服务器端要真正实现服务接口中的这些方法,ShirdrnServiceImpl类实现了ShirdrnService接口中列出的方法。

ShirdrnServiceImpl类的实现如下所示:

  1. package org.shirdrn.rmi.server;
  2. import java.io.IOException;
  3. import java.util.Date;
  4. public class ShirdrnServiceImpl implements ShirdrnService {
  5. public ShirdrnServiceImpl() {
  6. }
  7. public String getServerTime() throws IOException, ClassNotFoundException{
  8. Date date = new Date();
  9. String time = date.toLocaleString();
  10. return time;
  11. }
  12. public String getGreetings() throws IOException, ClassNotFoundException{
  13. Date date = new Date();
  14. int hour = date.getHours();
  15. String greetings = "";
  16. if(hour < 12 && hour > 6){
  17. greetings = "上午好!";
  18. }
  19. else if(hour <= 18 && hour > 12){
  20. greetings = "下午好!";
  21. }
  22. else{
  23. greetings = "晚上好!";
  24. }
  25. return greetings;
  26. }
  27. }

3、客户端Stub的实现

ShirdrnStub类实现了ShirdrnService接口,它是客户端获取服务结果数据最直接的实现。然而,它并没有与服务器端真正实现服务接口的实现类直接打交道,而是通过网络传输获取到调用方法的执行结果数据。

  1. package org.shirdrn.rmi.client;
  2. import java.io.IOException;
  3. import java.io.ObjectInputStream;
  4. import java.io.ObjectOutputStream;
  5. import java.net.Socket;
  6. import java.net.UnknownHostException;
  7. import org.shirdrn.rmi.server.ShirdrnService;
  8. public class ShirdrnStub implements ShirdrnService{
  9. Socket socket;
  10. public ShirdrnStub() {
  11. try {
  12. socket = new Socket("56987b31c0b246d",8888);
  13. } catch (UnknownHostException e) {
  14. e.printStackTrace();
  15. System.out.println("[信息]创建套接字异常:未知的主机名称。");
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. public String getServerTime() throws IOException, ClassNotFoundException {
  21. ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
  22. oos.writeObject("getServerTime");
  23. oos.flush();
  24. ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
  25. return (String)ois.readObject();
  26. }
  27. public String getGreetings() throws IOException, ClassNotFoundException {
  28. ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
  29. oos.writeObject("getGreetings");
  30. oos.flush();
  31. ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
  32. return (String)ois.readObject();
  33. }
  34. }

4、服务器端Skeleton的实现

ShirdrnSkeleton类继承了Thread,因此它可以启动服务器端线程,而且是单线程。在
ShirdrnSkeleton中,真正与提供服务实现的类进行了交互。在建立起连接以后,首先获取到客户端调用的方法,根据客户的选择来直接与服务的实
现交互,执行方法获得执行结果数据,从而将结果数据进行序列化,通过网络传输给客户完成应答过程。

实例化ShirdrnSkeleton类的一个实例后,启动线程,这很像是一个服务器端监听器,监听客户端动作,从而完成服务的请求。

  1. package org.shirdrn.rmi.server;
  2. import java.io.ObjectInputStream;
  3. import java.io.ObjectOutputStream;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6. public class ShirdrnSkeleton extends Thread {
  7. ShirdrnServiceImpl shirdrnServiceImpl;
  8. public ShirdrnSkeleton(ShirdrnServiceImpl shirdrnServiceImpl) {
  9. this.shirdrnServiceImpl = shirdrnServiceImpl;
  10. }
  11. public void run() {
  12. try {
  13. ServerSocket serverSocket = new ServerSocket(8888);
  14. Socket socket = serverSocket.accept();
  15. while(socket != null){
  16. ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
  17. String method = (String)ois.readObject();
  18. if(method.equals("getServerTime")){
  19. String serverTime = shirdrnServiceImpl.getServerTime();
  20. ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
  21. oos.writeObject(serverTime);
  22. oos.flush();
  23. }
  24. if(method.equals("getGreetings")){
  25. String greetings = shirdrnServiceImpl.getGreetings();
  26. ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
  27. oos.writeObject(greetings);
  28. oos.flush();
  29. }
  30. }
  31. } catch (Exception e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. public static void main(String[] args){
  36. ShirdrnServiceImpl ssi = new ShirdrnServiceImpl();
  37. ShirdrnSkeleton skeleton = new ShirdrnSkeleton(ssi);
  38. skeleton.start();
  39. }
  40. }

5、客户端测试程序

通过ShirdrnClient类中的主函数,实现了对服务器端提供的服务接口中的方法进行调用:

  1. package org.shirdrn.rmi.client;
  2. import java.io.IOException;
  3. import org.shirdrn.rmi.server.ShirdrnService;
  4. public class ShirdrnClient {
  5. public ShirdrnClient() {
  6. }
  7. public static void main(String[] args) throws IOException,
  8. ClassNotFoundException {
  9. ShirdrnService stub = new ShirdrnStub();
  10. System.out.println("正在获取服务器时间...");
  11. System.out.println("服务器时间 : "+stub.getServerTime());
  12. System.out.println("正在获取问候语...");
  13. System.out.println("问候语为 : "+stub.getGreetings());
  14. }
  15. }

6、测试过程

心得总结

1、客户端是直接通过实例化一个Stub,因为Stub实现了服务方暴露的接口,所以可以直接通过一个Stub的实例来调用服务方法。但是,这并没有直接调用服务方的服务接口的实现。

2、服务方具有服务接口的真正实现。而真正调用服务接口实现类是在服务器端的Skeleton中,在Skeleton中负责调用真正的服务实现,将执行结果返回给客户端,屏蔽了方法调用的实现细节。

3、客户端Stub和服务器端Skeleton之间建立起网络连接,进行了网络通信,主要就是对执行结果进行传输/接收,Skeleton将结果通过网络传输到客户端,而Stub通过代理接口接收传输而来的结果数据。

rmi 工作原理的更多相关文章

  1. Webservice工作原理及实例

    Web Service工作原理及实例   一.Web Service基本概念   Web Service也叫XML Web Service WebService是一种可以接收从Internet或者In ...

  2. 分布式的几件小事(二)dubbo的工作原理

    1.dubbo的工作原理 ①整体设计 图例说明: 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口. 图中从下至上分为十层,各层均为单 ...

  3. 菜鸟学Struts2——Struts工作原理

    在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...

  4. 【夯实Nginx基础】Nginx工作原理和优化、漏洞

    本文地址 原文地址 本文提纲: 1.  Nginx的模块与工作原理    2.  Nginx的进程模型    3 . NginxFastCGI运行原理        3.1 什么是 FastCGI   ...

  5. HashMap的工作原理

    HashMap的工作原理   HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和HashMap之间 ...

  6. 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 工作原理和相关组件(三)

    RAC 工作原理和相关组件(三) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体 ...

  7. ThreadLocal 工作原理、部分源码分析

    1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...

  8. Servlet的生命周期及工作原理

    Servlet生命周期分为三个阶段: 1,初始化阶段  调用init()方法 2,响应客户请求阶段 调用service()方法 3,终止阶段 调用destroy()方法 Servlet初始化阶段: 在 ...

  9. 代码管理工具 --- git的学习笔记二《git的工作原理》

    通过几个问题来学习代码管理工具之git 一.git是什么?为什么要用它?使用它的好处?它与svn的区别,在Mac上,比较好用的git图形界面客户端有 git 是分布式的代码管理工具,使用它是因为,它便 ...

随机推荐

  1. 三维点集拟合:平面拟合、RANSAC、ICP算法

    ACM算法分类:http://www.kuqin.com/algorithm/20080229/4071.html 一: 拟合一个平面:使用SVD分解,代码里面去找吧 空间平面方程的一般表达式为: A ...

  2. PCL:全程详解 VS2010+PCL配置

    浑浑噩噩半年时间,终于不得不干点和机器人有关的东西.ICRA和IROS推荐的三维图形库-点云库,几乎成了机器人视觉算法的标配. 参考了几篇文章,最后终于配置成功. 下面是文章地址: 这个有点看头,累积 ...

  3. 企业级任务调度框架Quartz(4) 多个job实例注册到任务调度器上

    前序:     在第一个例子我看到了自定义的作业类在任务调度器上注册后,则通过任务调度器来实现启动:下面,我们将同一个作业类执行两个任务,并都将他们注册到任务调度器上!     首先一个job类指向两 ...

  4. Steamroller FreeCodeCamp

    function steamroller(arr) { // I'm a steamroller, baby var resultArr = []; for(var i = 0; i < arr ...

  5. shell问题-报错即退出

    如下: #!/bin/bash set -o errexit 在最开头加上 set -o errexit 即可(或者 set -e) 要关闭的时候 set +o errexit        (或者 ...

  6. python的包装和授权

    包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均 ...

  7. [Ynoi2019模拟赛]Yuno loves sqrt technology II

    题目大意: 给定一个长为\(n\)的序列,\(m\)次询问,每次查询一个区间的逆序对数. 32MB. 解题思路: 出题人题解 众所周知lxl是个毒瘤,Ynoi道道都是神仙题 二次离线莫队. 对于每个区 ...

  8. Git clone时出现Please make sure you have the correct access rights and the repository exists.问题已解决。

    看了好多资料终于搞定了git 中clone命令报错这个问题,废话不多说直接上步骤希望对大家有帮助. 1   删除.ssh文件夹(直接搜索该文件夹)下的known_hosts(手动删除即可,不需要git ...

  9. 四大MQ比较及MQ详解

    消息队列已经逐渐成为企业IT系统内部通信的核心手段.它具有低耦合.可靠投递.广播.流量控制.最终一致性等一系列功能,成为异步RPC的主要手段之 一.当今市面上有很多主流的消息中间件,如老牌的Activ ...

  10. botot framework选择下拉框

    1,下拉框不能输入文字,如图: 方法: select from list    id=xxx   要选择的数据 2.下拉框可输入文字,如图: 方法: click element   di=xxx   ...