java RMI学习笔记RMI(Remote Method)Java RMI 威力强大Java远程消息交换协议JRMP使用RMI优点RMI网络模型网络模型RMI的工作原理RMI远程调用步骤:编码实现j

RMI(Remote Method)

Invocation):远程方法调用,即在RPC的基础上有向前迈进了一步,提供分布式对象间的通讯。允许运行在一个java虚拟机的对象调用运行在另一个java虚拟机上对象的方法。这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。

Java RMI 威力强大

Java RMI在JDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方案之一。其实它可以被看作是RPC的Java版本。但是传统RPC并不能很好地应用于分布式对象系统。而Java RMI则支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。

Java远程消息交换协议JRMP

RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信。由于JRMP是专为Java对象制定的,Java RMI具有Java的"Write Once,Run Anywhere"的优点,是分布式应用系统的百分之百纯Java解决方案。用Java RMI开发的应用系统可以部署在任何支持JRE(Java Run Environment Java,运行环境)的平台上。但由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信。

使用RMI优点

RMI大大增强了java开发分布式应用的能力,例如可以将计算方法复杂的程序放在其他的服务器上,主服务器只需要去调用,而真正的运算是在其他服务器上进行,最后将运算结果返回给主服务器,这样就减轻了主服务器的负担,提高了效率(但是也有其他的开销)。

RMI网络模型

在客户端为远程对象安装一个代理。代理是位于客户端虚拟机中的一个对象,它对于客户端程序来说,就像是要访问的远程对象一样。客户端调用此代理时,只需进行常规的方法调用。而客户端代理则负责使用网络协议与服务器进行联系。

网络模型

图片.png

RMI的工作原理

一、术语介绍

1、存根:当客户端要调用远程对象的一个方法时,实际上调用的是代理对象上的一个普通方法,我们称此代理对象为存根(stub)。存根位于客户端机器上,而非服务器上。

2、参数编组:存根会将远程方法所需的参数打包成一组字节,对参数编码的过程就称为参数编组。参数编组的目的是将参数转换成适合在虚拟机之间进行传递的格式,在RMI协议中,对象是使用序列化机制进行编码的。

RMI远程调用步骤:

1,客户对象调用客户端辅助对象上的方法

2,客户端辅助对象打包调用信息(变量,方法名),通过网络发送给服务端辅助对象

3,服务端辅助对象将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象

4,调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象

5,服务端辅助对象将结果打包,发送给客户端辅助对象

6,客户端辅助对象将返回值解包,返回给客户对象

7,客户对象获得返回值

对于客户对象来说,步骤2-6是完全透明的

编码实现java RMI hello world的例子

定义一个接口

/**

* Created by junyi.pc on 2017/1/23.

*/

importjava.rmi.RemoteException;

importjava.rmi.*;

public interface IHello extends Remote {

/**

* 简单的返回“Hello World!"字样

*

*@return返回“Hello World!"字样

*@throwsjava.rmi.RemoteException

*/

public default String helloWorld()throwsRemote Exception {

return null;

}

/**

* 一个简单的业务方法,根据传入的人名返回相应的问候语

*@paramsomeBodyName人名

*@return返回相应的问候语

*@throwsjava.rmi.RemoteException

*/

publicString sayHelloToSomeBody(String someBodyName)throwsRemoteException;

}

接口实现

/**

* Created by junyi.pc on 2017/1/23.

*/

importjava.rmi.RemoteException;

importjava.rmi.*;

importjava.rmi.server.UnicastRemoteObject;

public class HelloImpl extends UnicastRemoteObject implements IHello {

/**

* 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须声明抛出RemoteException异常

*

*@throwsRemoteException

*/

public HelloImpl()throws RemoteException {

}

/**

* 简单的返回“Hello World!"字样

*

*@return返回“Hello World!"字样

*@throwsjava.rmi.RemoteException

*/

publicString helloWorld()throwsRemoteException {

return"Hello World!";

}

/**

* 一个简单的业务方法,根据传入的人名返回相应的问候语

*

*@paramsomeBodyName人名

*@return返回相应的问候语

*@throwsjava.rmi.RemoteException

*/

publicString sayHelloToSomeBody(String someBodyName)throwsRemoteException {

return"你好,"+ someBodyName +"!";

}

}

server实现

/**

* Created by junyi.pc on 2017/1/23.

*/

importjava.net.MalformedURLException;

importjava.rmi.AlreadyBoundException;

importjava.rmi.Naming;

importjava.rmi.RemoteException;

importjava.rmi.registry.LocateRegistry;

public classHelloServer {

public static voidmain(String args[]) {

try{

//创建一个远程对象

IHello rhello =new HelloImpl();

//本地主机上的远程对象注册表Registry的实例,并指定端口为8888,这一步必不可少(Java默认端口是1099),必不可缺的一步,缺少注册表创建,则无法绑定对象到远程注册表上

LocateRegistry.createRegistry(8888);

//把远程对象注册到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();

}

}

}

客户端测试程序

/**

* Created by junyi.pc on 2017/1/23.

*/

importjava.net.MalformedURLException;

importjava.rmi.Naming;

importjava.rmi.NotBoundException;

importjava.rmi.RemoteException;

/**

* Created by junyi.pc on 2017/1/23.

*/

public class client {

public static voidmain(String args[]){

try{

//在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法

IHello rhello =(IHello) Naming.lookup("rmi://localhost:8888/RHello");

System.out.println(rhello.helloWorld());

System.out.println(rhello.sayHelloToSomeBody("张俊怡"));

}catch(NotBoundException e) {

}catch(MalformedURLException e) {

e.printStackTrace();

e.printStackTrace();

}catch(RemoteException e) {

e.printStackTrace();

}

}

}

运行结果

A1VY8T$)~D`)1WQR`$3B~R7.png

![}J24D(99_)8VE%PEJ68PMN.png

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏LIN_ZONE

composer 实现自动加载原理

一般在框架中都会用到composer工具,用它来管理依赖。其中composer有类的自动加载机制,可以加载composer下载的库中的所有的类文件。那么comp...

993
来自专栏技术墨客

Spring核心——Bean的依赖注入 原

在设计模式与IoC这篇文章中,介绍了Spring基础的三大支柱的两项内容——IoC、Bean。本篇将继续围绕着Bean的创建时的注入方式来介绍Spring的核心...

931
来自专栏木木玲

Netty 源码解析 ——— 服务端启动流程 (上)

2946
来自专栏Java学习123

一个简单的AXIS远程调用Web Service示例

4887
来自专栏菩提树下的杨过

基于spring boot 2.x的websocket示例

spring boot 2/spring 5自带了websocket,下面是最基本的示例(包括java服务端、java客户端以及js客户端)

5442
来自专栏码代码的陈同学

jstack是如何获取threaddump的?

JDK提供了许多命令行工具用于监视JVM,让我们可以了解其异常堆栈、GC日志、threaddump、heapdump等信息。一时好奇,想看看jstack是如何实...

2305
来自专栏JavaQ

高并发编程-CyclicBarrier深入解析

CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到所有线程都到达某个公共屏障点(也可以叫同步点),即相互等待的线程都完成调用await方法...

1K3
来自专栏Jack-Cui

Linux应用层系统时间写入RTC时钟的方法

Linux内核版本:linux-3.0.35 开发板:i.MX6S MY-IMX6-EK200 系统:Ubuntu12 前言:之前写过一篇关于如...

2220
来自专栏冰霜之地

如何设计并实现一个线程安全的 Map ?(下篇)

在上篇中,我们已经讨论过如何去实现一个 Map 了,并且也讨论了诸多优化点。在下篇中,我们将继续讨论如何实现一个线程安全的 Map。说到线程安全,需要从概念开始...

5237
来自专栏java学习

关于Spring面试题讲解2

依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置...

892

扫码关注云+社区

领取腾讯云代金券