【前面的话】

最近过的好舒服,每天过的感觉很充实,一些生活和工作的技巧注意了就会发现,其实生活也是可以过的如此的有滋有味,满足现在的状况,并且感觉很幸福。

学习java RMI的原因是最近在使用dubbo框架做一个系统,所以对这java RMI进行学习,做一些笔记,基础性文章,选择性阅读。

【定义】

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

我们知道远程过程调用(Remote Procedure Call, RPC)可以用于一个进程调用另一个进程(很可能在另一个远程主机上)中的过程,从而提供了过程的分布能力。Java 的 RMI 则在 RPC 的基础上向前又迈进了一步,即提供分布式对象间的通讯。

RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。

这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。

JavaRMI

一、工作原理

RMI能让一个Java程序去调用网络中另一台计算机的Java对象的方法,那么调用的效果就像是在本机上调用一样。通俗的讲:A机器上面有一个class,通过远程调用,B机器调用这个class 中的方法。

RMI,远程方法调用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java应用程序的方便途径。RMI是非常容易使用的,但是它非常的强大。

RMI的基础是接口,RMI构架基于一个重要的原理:定义接口和定义接口的具体实现是分开的。下面我们通过具体的例子,建立一个简单的远程计算服务和使用它的客户程序

二、RMI包含部分:

  1. 远程服务的接口定义
  2. 远程服务接口的具体实现
  3. 桩(Stub)和框架(Skeleton)文件
  4. 一个运行远程服务的服务器
  5. 一个RMI命名服务,它允许客户端去发现这个远程服务
  6. 类文件的提供者(一个HTTP或者FTP服务器)
  7. 一个需要这个远程服务的客户端程序

三、RMI的用途?

RMI的用途是为分布式Java应用程序之间的远程通信提供服务,提供分布式服务。

目前主要应用时封装在各个J2EE项目框架中,例如Spring,EJB(Spring和EJB均封装了RMI技术)

在Spring中实现RMI:

①在服务器端定义服务的接口,定义特定的类实现这些接口;

②在服务器端使用org.springframework.remoting.rmi.RmiServiceExporter类来注册服务;

③在客户端使用org.springframework.remoting.rmi.RmiProxyFactoryBean来实现远程服务的代理功能;

④在客户端定义访问与服务器端服务接口相同的类

四、RMI的局限?                                                                    

RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信。JRMP是专为Java的远程对象制定的协议,由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信(意思是只支持客户端和服务器端都是Java程序的代码的远程调用)。

五、RMI的使用局限?

由于客户机和服务器都是使用Java编写的,二者平台兼容性的要求仅仅是双方都运行在版本兼容的Java虚拟机上。

六、RMI调用远程方法的参数和返回值

当调用远程对象上的方法时,客户机除了可以将原始类型的数据作为参数一外,还可以将对象作为参数来传递,与之相对应的是返回值,可以返回原始类型或对象,这些都是通过Java的对象序列化(serialization)技术来实现的。(换而言之:参数或者返回值如果是对象的话必须实现Serializable接口)

七、 RMI应用程序的基本模型

八、RMI体系结构

桩/框架(Stub/Skeleton)层:客户端的桩和服务器端的框架;

远程引用(remote reference)层:处理远程引用行为

传送层(transport):连接的建立和管理,以及远程对象的跟踪

九、 RMI类和接口(完成一个简单RMI需要用到的类)。

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

Public interface Remote{}

在RMI中,远程接口声明了可以从远程Java虚拟机中调用的方法集。远程接口满足下列要求:

1、远程接口必须直接或间接扩展Java.rmi.Remote接口,且必须声明为public,除非客户端于远程接口在同一包中

2、在远程接口中的方法在声明时,除了要抛出与应用程序有关的一场之外,还必须包括RemoteException(或它的超类,IOExcepion或Exception)异常

3、在远程方法声明中,作为参数或返回值声明的远程对象必须声明为远程接口,而非该接口的实现类。

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

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

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

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

  1. Registry registry=LocateRegisty.getRegistry();//Registry是个接口,他继承了Remote,此方法返回本地主机在默认注册表端口 1099 上对远程对象 Registry 的引用。
  2. getRegistry(int port) 返回本地主机在指定 port 上对远程对象 Registry 的引用;
  3. getRegistry(String host)  返回指定 host 在默认注册表端口 1099 上对远程对象 Registry 的引用;
  4. getRegistry(String host, int port) 返回指定的 hostport 上对远程对象 Registry 的引用
  5. registry.bind(“I serve”,service);// bind(String name,Remote obj) 绑定对此注册表中指定 name 的远程引用。name : 与该远程引用相关的名称 obj : 对远程对象(通常是一个 stub)的引用
  6. unbind(String name)移除注册表中指定name的绑定。
  7. rebind(String name,Remote obj)重新绑定,如果name已存在,但是Remote不一样则替换,如果Remote一样则丢弃现有的绑定
  8. lookup(String name) 返回注册表中绑定到指定 name 的远程引用,返回Remote
  9. String[] list()   返回在此注册表中绑定的名称的数组。该数组将包含一个此注册表中调用此方法时绑定的名称快照。

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

Registry registry=LocateRegisty.getRegistry();
SomeService servcie=(SomeService)registry.lookup(“I serve”);
Servcie.requestService();

(四) Naming和Registry类类似。

客户端:

Naming.lookup(String url)

url 格式如下"rmi://localhost/"+远程对象引用
服务器端:
     Registry registry=LocateRegistry.createRegistry(int port);
     Naming.rebind(“service”,service);

(五) RMISecurityManager

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

在从远程主机下载代码之前必须执行以下代码来安装RMISecurityManager:

System.setSecurityManager(new RMISecurityManager());

十、demo开发

为了编写一个demo,我们分为两部分,一部分是server端的代码,一部分是client端的代码,client端的代码主要是为了使用server端的代码。当然这个代码是非常简单的,只是为了说明问题,现实的使用会使比较复杂的。

(一) 我们的目的

建立一个server端的java project,包含远程端的代码,定义接口,定义接口实现,然后在建立一个client端的java project,通过RMI使用远端服务中的方法。

(二) 我们的代码结构

(三) 远程服务代码

1. 远程服务的接口定义

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

UserManagerInterface.java

 package cn.com.tt.rmiserver.stub;

 import java.rmi.Remote;
import java.rmi.RemoteException; import cn.com.tt.rmiserver.bean.Account; public interface UserManagerInterface extends Remote{
public String getUserName() throws RemoteException;
public Account getAdminAccount() throws RemoteException;
}

接口必须继承Remote类,每一个定义地方法都要抛出RemoteException异常对象。

2. 接口的具体实现

第二步就是对于上面的接口进行实现:

UserManagerImp.java

 package cn.com.tt.rmiserver;

 import java.rmi.RemoteException;

 import cn.com.tt.rmiserver.stub.UserManagerInterface;
import cn.com.tt.rmiserver.bean.Account; public class UserManagerImp implements UserManagerInterface {
public UserManagerImp() throws RemoteException { }
private static final long serialVersionUID = -3111492742628447261L; public String getUserName() throws RemoteException{
return "TT";
}
public Account getAdminAccount() throws RemoteException{
Account account=new Account();
account.setUsername("TT");
account.setPassword("123456");
return account;
}
}

3. 定义一个bean,实现implements Serializable序列化接口。也就是可以在client和server端进行传输的可序列化对象。

Account.java

 package cn.com.tt.rmiserver.bean;

 import java.io.Serializable;

 public class Account implements Serializable,Cloneable{
private static final long serialVersionUID = -1858518369668584532L;
private String username;
private String password; public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

4. 定义server端的主程序入口。

Entry.java

 package cn.com.tt.rmiserver.entry;

 import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject; import cn.com.tt.rmiserver.UserManagerImp;
import cn.com.tt.rmiserver.stub.UserManagerInterface; public class Entry {
public static void main(String []args) throws AlreadyBoundException, RemoteException{
UserManagerImp userManager=new UserManagerImp();
UserManagerInterface userManagerI=(UserManagerInterface)UnicastRemoteObject.exportObject(userManager,0);
// Bind the remote object's stub in the registry
Registry registry = LocateRegistry.createRegistry(2002); registry.rebind("userManager", userManagerI);
System.out.println("server is ready");
}
}

(四) client端代码

  1. 把Server端的Account类和接口UserManagerInterface 导出Export成jar包,命名为:RmiServerInterface.jar。导入到client中。
  2. 项目——右键——Export——java——jar file——next——选择Account类和接口UserManagerInterface——命名为:RmiServerInterface.jar如下图:

3. 新建一个java Project,导入jar包,编写客户端代码。

4. 代码

ClientEntry.java

 package weiblog.rmi;

 import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry; import cn.com.tt.rmiserver.stub.UserManagerInterface; public class ClientEntry { public static void main(String []args){ try {
Registry registry = LocateRegistry.getRegistry("localhost",2004);
UserManagerInterface userManager = (UserManagerInterface)registry.lookup("userManager");
System.out.println("用户名是"+userManager.getAdminAccount().getUsername()
+"密码"+userManager.getAdminAccount().getPassword());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotBoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } }

5. 先运行服务器端代码, 然后运行客户端代码,就会显示运行结果,客户端可以运行多次,每次都可以取得服务器端的对象。如果要再次运行客户端代码就需要更改端口号,如果不更改就会显示端口号被占用。

【参考资料】

1. demo参考的下面文章:

RMI网络编程开发之二 如何搭建基于JDK1.5的分布式JAVA RMI 程序

2. 理论知识参考的下面文章:

RMI入门教程

java_RMI技术讲解

【后面的话】

快要放假了多么值得高兴。

时至今日都是我咎由自取,学java那是自找,与任何人无关。大学生活过的平顺,造就了我轻信java,编写众多bug的脾气,导致今日岌岌可危的地步,我今天愿意承担一切后果。其实,我很感激你们让我在测试的时候发现bug,而不是到了生产环境,我必须重新梳理我所造成的bug,坦然面对每个bug。我,在学java的路上编写了大量的bug。我辜负了c和c++,辜负了C#,辜负了程序员和码农的称呼,辜负了所有以为我可以写出漂亮代码的人。对不起,请接收我发自内心的歉意和忏悔。晚上和周末本来有一个温暖和美的生活,可是这一切被我学习java给打破了,我学java的行为不配得到原谅,我造成的无休止的bug现状也难以修改,但我还是想修改,我必须修改,这是我今日之后的生活。至于我自己,已咎由自取,愿日后不在产生bug。java程序员。——我真是越来越喜欢自黑了。哈哈,让我笑一会。

——TT

Java学习笔记(十六)——Java RMI的更多相关文章

  1. Java学习笔记十六:Java中的构造方法

    Java中的构造方法 1.使用new+构造方法 创建一个新的对象: 2.构造方法是定义在Java类中的一个用来初始化对象的方法: 3.构造方法与类同名且没有返回值: 4.语法格式: public 构造 ...

  2. Java学习笔记十五:Java中的成员变量和局部变量

    Java中的成员变量和局部变量 一:成员变量: 成员变量在类中定义,用来描述对象将要有什么 成员变量可以被本类的方法使用,也可以被其他类的方法使用,成员变量的作用域在整个类内部都是可见的 二:局部变量 ...

  3. Java 读书笔记 (十六) Java 继承

    例: 开发动物类,其中动物分别为企鹅以及老鼠,要求如下: 企鹅: 属性(姓名,id), 方法(吃,睡,自我介绍) 老鼠: 属性(姓名,id), 方法(吃,睡,自我介绍) 企鹅类: public cla ...

  4. Java学习笔记十九:Java中的访问控制修饰符

    Java中的访问控制修饰符 一:Java修饰符的种类: 访问修饰符 非访问修饰符 修饰符用来定义类.方法或者变量,通常放在语句的最前端.我们通过下面的例子来说明: public class Hello ...

  5. Java学习笔记十八:Java面向对象的三大特性之封装

    Java面向对象的三大特性之封装 一:面向对象的三大特性: 封装 继承 多态   二:封装的概念: 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访 ...

  6. Java学习笔记十二:Java中方法的重载

    Java中方法的重载 什么是方法的重载呢? 如果同一个类中包含了两个或两个以上方法名相同.方法参数的个数.顺序或类型不同的方法,则称为方法的重载,也可称该方法被重载了.如下所示 4 个方法名称都为 s ...

  7. 201671010140. 2016-2017-2 《Java程序设计》java学习第十六周

    java学习第十六周-并发        本周,学习了Java中线程,并发的知识,在老师的带领下,进行了对知识的理解学习,以及对实验的运行讲解,对这一块内容掌握的还可以,在自主编程中,也能够完成.线, ...

  8. python3.4学习笔记(十六) windows下面安装easy_install和pip教程

    python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...

  9. Java学习笔记心得——初识Java

    初识Java 拿到这本厚厚的<Java学习笔记>,翻开目录:Java平台概论.从JDK到TDE.认识对象.封装.继承与多态...看着这些似懂非懂的术语名词,心里怀着些好奇与担忧,就这样我开 ...

  10. Java 学习笔记 ------第六章 继承与多态

    本章学习目标: 了解继承的目的 了解继承与多态的关系 知道如何重新定义方法 认识java.lang.object 简介垃圾回收机制 一.继承 继承是java面向对象编程技术的一块基石,因为它允许创建分 ...

随机推荐

  1. 剑指Offer 树的子结构

    题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构)     思路: 分为2个部分.1先找出A中和B根节点相同的节点r. 2,咱判断B中所有孩子节点是不 ...

  2. 转:linux下bwa和samtools的安装与使用

    bwa的安装流程安装本软体总共需要完成以下两个软体的安装工作:1) BWA2) Samtools1.BWA的安装a.下载BWA (download from BWA Source Forge ) ht ...

  3. c语言——gcc,make,vim

    在linux环境下进行c语言编程,基础的工具就是gcc和make.关于这2个工具的内容有很多,现在要做的仅仅是起步. gcc的用法: -S Compile only; do not assemble ...

  4. OS10.11系统下 安装cocoapods 以及 安装cocoapods-xcode-plugin-master插件来加载三方框架

    http://www.cnblogs.com/cheng923181/p/4883476.html OS10.11系统下 安装cocoapods 以及 安装cocoapods-xcode-plugin ...

  5. NFV技术中遇到的新名词

    NUMA topology:Non-Uniform Memory Access (NUMA) is a computer system architecture that is used with m ...

  6. Vim保存只读模式下的修改

    用 vim 打开没有写权限的文件而忘记用 sudo 时,文件变成 read-only这时候用 :w!, SHIFT+ZZ,或者 qw! 都无法保存 :w !sudo tee % :w : Write ...

  7. 【云计算】marathon集群如何升级?

    Upgrading to a Newer Version We generally recommend creating a backup of the ZooKeeper state before ...

  8. 【leetcode】Unique Binary Search Trees

    Unique Binary Search Trees Given n, how many structurally unique BST's (binary search trees) that st ...

  9. linux mysql查看安装信息

    ps -ef|grep mysql root               ?        :: /bin/sh /usr/bin/mysqld_safe --datadir=/var/lib/mys ...

  10. Robot Framework + Selenium2Library环境下,结合Selenium Grid实施分布式自动化测试

    最近一段时间,公司在推行自动化测试流程,本人有幸参与了自定义通用控件的关键字封装和脚本辅助编写.数据驱动管理.测试用例执行管理等一系列工具软件的研发工作,积累了一些经验,在此与大家做一下分享,也算是做 ...