Java——RMI

之前分布式系统调用用的是比较老的EJB,当时还是作为服务调用方,去调用别的系统的服务。最近发现新公司里面,用的是RMI,查了下发现EJB的底层实现就是RMI,也算是熟悉了。。。

一,使用JDK 中的RMI实现服务发布和引用

服务端接口:

/**
 * Created by LiuHuiChao on 2016/11/18.
 */
public interface UserInfoService extends Remote{
    /**
     * 定义远程接口,必须继承Remote接口,
     * 其中所有需要远程调用的方法都必须抛出RemoteException异常
     */
    String getUserName(int id) throws RemoteException;
}

服务端接口实现:

/**
 * Created by LiuHuiChao on 2016/11/18.
 */
public class UserInfoServiceImpl extends UnicastRemoteObject implements UserInfoService{

    /**
     * 接口的实现类同时要实现Serializable接口,这里继承UnicastRemoteObject也是间接实现Serializable接口,
     * 同时,因为构造方法需要抛出RemoteException,所以不能缺省使用隐含的无参构造方法,而应该自己显式定义构造方法。
     * @throws RemoteException
     */
    protected UserInfoServiceImpl() throws RemoteException {
    }

    @Override
    public String getUserName(int id) throws RemoteException {
        return "漠漠水田飞白鹭";
    }
}

单元测试服务启动与客户端调用:

**
 * Created by LiuHuiChao on 2016/11/18.
 */
public class JdkRmiServiceClientTest {

    /**
     * 使用Java 原生JDK实现RMI调用:服务端
     * @throws RemoteException
     * @throws AlreadyBoundException
     * @throws MalformedURLException
     */
    @Test
    public void startServer() throws RemoteException, AlreadyBoundException, MalformedURLException, InterruptedException {

        Object lock = new Object();
        synchronized (lock) {

            UserInfoService userInfoService=new UserInfoServiceImpl();//创建一个远程对象
            //生成远程对象注册表Registry的实例,并指定端口为8888(默认端口是1099)
            LocateRegistry.createRegistry(8888);
            //把远程对象注册到RMI服务器上,名称为UserInfoService
            ////绑定的URL标准格式为:rmi://host:port/name(协议名可以省略)
            Naming.bind("rmi://127.0.0.1:8888/UserInfoService",userInfoService);

            lock.wait();
        }
    }

    @Test
    public  void startClient() throws RemoteException, NotBoundException, MalformedURLException {
        // 在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法
        UserInfoService userInfoService=(UserInfoService)Naming.lookup("rmi://127.0.0.1:8888/UserInfoService");
        System.out.println(userInfoService.getUserName(1));
    }
}

在使用中,一般是服务端把接口打包成Jar,之后客户端在pom里面配置依赖这个jar,就能使用服务端接口了。

二,Spring集成RMI

为了方便服务发布与调用,可以使用Spring进行集成,我们只需编写普通POJO类就能实现;

服务端接口:

/**
 * Created by LiuHuiChao on 2016/11/18.
 */
public interface AccountService {
    int queryBalance(String mobileNo);
    String shoopingPayment(String mobileNo, byte protocol);
}

服务端实现类:

/**
 * Created by LiuHuiChao on 2016/11/18.
 */
public class MobileAccountServiceImpl implements AccountService  {

    public int queryBalance(String mobileNo) {
        if (mobileNo != null)
            return 100;
        return 0;
    }

    public String shoopingPayment(String mobileNo, byte protocol) {
       return mobileNo;
    }
}

服务端的Spring配置:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">

	<bean id="serviceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter">
		<property name="serviceName" value="MobileAccountService"/><!--服务名称-->
		<property name="service" ref="accountService"/> <!--配置注入的实现类-->
		<property name="serviceInterface"
				  value="test.Rmi.AccountService" />
		<property name="registryPort" value="8089" /> <!--调用的端口-->
		<property name="servicePort" value="8088" />
	</bean>

	<bean id="accountService" class="test.Rmi.MobileAccountServiceImpl" />

</beans>

在测试代码中启动服务端:

/**
 * Created by LiuHuiChao on 2016/11/18.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/applicationContext-test-service-rmi.xml"})
public class StartServerTest {

    @Test
    public void startServerTest() throws InterruptedException {
        Object lock = new Object();
        synchronized (lock) {
            lock.wait();
        }
    }
}

客户端Spring的配置:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">

	<bean id="mobileAccountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
		<property name="serviceUrl" value="rmi://127.0.0.1:8089/MobileAccountService" /> <!--调用的RMI服务的地址-->
		<property name="serviceInterface"
				  value="test.Rmi.AccountService" /> <!--调用的服务的接口-->
		<property name="refreshStubOnConnectFailure" value="true"/> <!--解决重启 rmi 的服务器后会出现拒绝连接或找不到服务对象的错误-->
		<!--
			 stub查询的另一个问题是,目标RMI服务器和RMI注册项在查询时要为可用的。如果客户端在服务器启动之前,
			 尝试查询和缓存该服务stub,那么客户端的启动将会失败(即使还不需要该服务)。
			 为了能够惰性查询服务stub,设定RmiProxyFactoryBean的lookupStubOnStarup标志为false。
			 然后在第一次访问 时查询该stub,也就是说,当代理上的第一个方法被调用的时候去主动查询stub,
			 同时被缓存。这也有一个缺点,就是直到第一次调用,否则无法确认目标 服务是否实际存在。
		-->
		<property name="lookupStubOnStartup" value="false"/>
	</bean>

</beans>

客户端调用代码:

/**
 * Created by LiuHuiChao on 2016/11/18.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/applicationContext-test-client-rmi.xml"})
public class StartClientTest {

    @Resource
    AccountService accountService;

    @Test
    public void testRmiAtClient(){
        String result = accountService.shoopingPayment("123333456357654", (byte) 5);
        System.out.println(result);
    }

}

三,RMI原理

RMI应用程序通常包括两个独立的程序:服务器程序和客户机程序。典型的服务器应用程序将创建多个远程对象,使这些远程对象能够被引用,然后等待客户机调用这些远程对象的方法。而典型的客户机程序则从服务器中得到一个或多个远程对象的引用,然后调用远程对象的方法。RMI为服务器和客户机进行通信和信息传递提供了一种机制。

在与远程对象的通信过程中,RMI使用标准机制:stub和skeleton。远程对象的stub担当远程对象的客户本地代表或代理人角色。调用程序将调用本地stub的方法,而本地stub将负责执行对远程对象的方法调用。在RMI中,远程对象的stub与该远程对象所实现的远程接口集相同。调用stub的方法时将执行下列操作:

(1) 初始化与包含远程对象的远程虚拟机的连接;

(2) 对远程虚拟机的参数进行编组(写入并传输);

(3) 等待方法调用结果;

(4) 解编(读取)返回值或返回的异常;

(5) 将值返回给调用程序。

为了向调用程序展示比较简单的调用机制,stub将参数的序列化和网络级通信等细节隐藏了起来。在远程虚拟机中,每个远程对象都可以有相应的skeleton(在JDK1.2环境中无需使用skeleton)。Skeleton负责将调用分配给实际的远程对象实现。它在接收方法调用时执行下列操作:(1) 解编(读取)远程方法的参数;(2) 调用实际远程对象实现上的方法;(3) 将结果(返回值或异常)编组(写入并传输)给调用程序。stub和skeleton由rmic编译器生成。

方法调用从客户对象经占位程序(Stub)、远程引用层(RemoteReference Layer)和传输层(Transport Layer)向下,传递给主机,然后再次经传输层,向上穿过远程调用层和骨干网(Skeleton),到达服务器对象。 占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。远程引用层处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。传输层管理实际的连接,并且追追踪可以接受方法调用的远程对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的传输层传递回客户端,再向上经传输层和远程调用层返回。最后,占位程序获得返回值。

四,RMI与RPC

 1,语言差异:RMI只支持Java系统间调用,而RPC支持异构系统调用;

 2,如何被调用的:


在RMI中,远程接口使每个远程方法都具有方法签名。如果一个方法在服务器上执行,但是没有相匹配的签名被添加到这个远程接口上,那么这个新方法就不能被RMI客户方所调用。

在RPC中,当一个请求到达RPC服务器时,这个请求就包含了一个参数集和一个文本值,通常形成“classname.methodname”的形式。这就向RPC服务器表明,被请求的方法在为“classname”的类中,名叫“methodname”。然后RPC服务器就去搜索与之相匹配的类和方法,并把它作为那种方法参数类型的输入。这里的参数类型是与RPC请求中的类型是匹配的。一旦匹配成功,这个方法就被调用了,其结果被编码后返回客户方。

3,传递信息直接的差异


在RMI中,远程接口使每个远程方法都具有方法签名。如果一个方法在服务器上执行,但是没有相匹配的签名被添加到这个远程接口上,那么这个新方法就不能被RMI客户方所调用。

在RPC中,当一个请求到达RPC服务器时,这个请求就包含了一个参数集和一个文本值,通常形成“classname.methodname”的形式。这就向RPC服务器表明,被请求的方法在为“classname”的类中,名叫“methodname”。然后RPC服务器就去搜索与之相匹配的类和方法,并把它作为那种方法参数类型的输入。这里的参数类型是与RPC请求中的类型是匹配的。一旦匹配成功,这个方法就被调用了,其结果被编码后返回客户方。

时间: 2024-10-11 16:41:07

Java——RMI的相关文章

java RMI入门指南

感觉这篇文章不错,直接转了 RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体如今它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方式之中的一个.事实上它能够被看作是RPC的Java版本号.可是传统RPC并不能非常好地应用于分布式对象系统.而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信.实现远程对象之间的无缝远程调用. RMI眼下使用Java远程消息交换协议JRMP

Metasploit溢出java RMI SERVER命令执行漏洞

利用java RMI SERVER命令执行漏洞,获得目标主机root权限. Java RMI Server的RMI注册表和RMI激活服务的默认配置存在安全漏洞,可被利用导致代码执行. 一.利用nmap工具扫描目标主机 1.1使用nmap命令对目标主机进行扫描.单击桌面空白处,右键菜单选择"在终端中打开". 1.2在终端中输入命令"nmap –sV  192.168.1.3",对目标主机进行端口扫描,发现开放1099端口. 1.3在终端中输入命令"msfco

JAVA RMI分布式原理和应用

RMI(Remote Method Invocation)是JAVA早期版本(JDK 1.1)提供的分布式应用解决方案,它作为重要的API被广泛的应用在EJB中.随着互联网应用的发展,分布式处理任务也随之复杂起 来,WebService也得到普遍的推广和应用.        在某些方面,例如跨语言平台的分布式应用,RMI就显得力不从心了.在实际的应用中,是采用WebService还是传统的RMI来实现?这是一个需要权衡的问题,两者的比较如下所述:        1. 比起WebService,它

Java RMI之HelloWorld篇

Java RMI之HelloWorld篇 Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方法调用的任何对象必须实现该远程接口. Java RMI不是什么新技术(在Java1.1的时代都有了),但却是是非常重要的底层技术. 大名鼎鼎的EJB都是建立在rmi基础之上的,现在还有一些开源的远程调用组件,其底层技术也是rmi. 在大力鼓吹Web Serv

对JAVA RMI的认识

RMI的定义 RPC (Remote Procedure Call):远程方法调用,用于一个进程调用另一个进程中的过程,从而提供了过程的分布能力. RMI(Remote Method Invocation):远程方法调用,即在RPC的基础上有向前迈进了一步,提供分布式对象间的通讯.允许运行在一个java 虚拟机的对象调用运行在另一个java虚拟机上对象的方法.这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中. RMI的全称宗旨就是尽量简化远程接口对象的调用.

java rmi的一个简单实例

参考网站  http://www.cnblogs.com/leslies2/archive/2011/05/20/2051844.html 实体类PersonEntity package net.cs30.rmi; import java.io.Serializable; /** * Created by caochenghua on 2017/4/4. */ public class PersonEntity implements Serializable { private int id;

Java RMI 入门案例

Java Remote Method Invocation(Java RMI) 是一个 Java API, 执行远程方法的调用,相当于 Remote Procedure Calls(RPC).Java RMI 支持直接传输序列化的 Java 类,以及分布式的垃圾回收. 案例概况 实现一个简单的 Java RMI 可通过以下四步骤: 1. 启动 rmiregistry 2. 定义服务接口,实现服务接口,通过 rmic 工具创建存根文件( stub ) 3. 实现服务端功能,并启动 4. 实现客户端

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

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

Java RMI 用法总结

RMI就是远程方法调用的简写.顾名思义,就是让一台机器上的对象调用另外一个机器上的对象.RMI的用法非常简单,首先是服务端定义一个接口(接口要扩展Remote接口),再实现这个接口(要扩展UnicastRemoteObject),再绑定到Naming静态类中.客户端通过Naming获取一个远程对象,就可以像普通的对象一样调用远程对象了.RMI中有个Stub类,它的作用就是代理服务器的接口对象,负责将方法的调用转换成网络请求发送给服务器,再从服务器返回对象进行解码.在JDK1.5中,Stub类会自

启动tomcat时 错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099;

错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099; nested exception is:  java.net.BindException: Address already in use: JVM_BindDisconnected from server 端口被占用,进入windows命令,查看什么进程被占用了 netstat -aon|findstr 1099 显示. 然后关闭占用该端口的进程, t