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));
    }
}
二,Spring集成RMI
/**
 * 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;
    }
}
<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
在RPC中,当一个请求到达RPC服务器时,这个请求就包含了一个参数集和一个文本值,通常形成“classname.methodname”的形式。这就向RPC服务器表明,被请求的方法在为“classname”的类中,名叫“methodname”。然后RPC服务器就去搜索与之相匹配的类和方法,并把它作为那种方法参数类型的输入。这里的参数类型是与RPC请求中的类型是匹配的。一旦匹配成功,这个方法就被调用了,其结果被编码后返回客户方。
3,传递信息直接的差异
在RMI中,远程接口使每个远程方法都具有方法签名。如果一个方法在服务器上执行,但是没有相匹配的签名被添加到这个远程接口上,那么这个新方法就不能被RMI客户方所调用。
在RPC中,当一个请求到达RPC服务器时,这个请求就包含了一个参数集和一个文本值,通常形成“classname.methodname”的形式。这就向RPC服务器表明,被请求的方法在为“classname”的类中,名叫“methodname”。然后RPC服务器就去搜索与之相匹配的类和方法,并把它作为那种方法参数类型的输入。这里的参数类型是与RPC请求中的类型是匹配的。一旦匹配成功,这个方法就被调用了,其结果被编码后返回客户方。
Java——RMI的更多相关文章
- Exception thrown by the agent : java.rmi.server.ExportException: Port already in use
		今天有个应用一直起不来,感觉配置都对啊,奇了怪了.看日志发现如下: STATUS | wrapper | 2017/01/04 08:09:31 | Launching a JVM...INFO | ... 
- Java RMI之HelloWorld篇
		Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ... 
- 启动tomcat时 错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099;
		错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099; nested exception is: java ... 
- JAVA RMI 实例
		下面我将介绍一个完整的实例,让初学者能快速体验RMI的功用. 分为以下四个步骤 1. 创建远程接口及声明远程方法(HelloInterface.java)2. 实现远程接口及远程方法(继承Unicas ... 
- JAVA RMI例子
		RMI 是java语言的一个RPC框架,本文给出基础例子如下: 1.实现接口: public interface ICalc extends Remote { public int add(int p ... 
- java.rmi.NoSuchObjectException: no such object in table
		jmx链接的时候,最简单的例子都行不通,郁闷,出现了: 参考:http://reiz6153.blog.163.com/blog/static/401089152009442723208/ 代码: M ... 
- 启动tomcat时 错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099的解决办法
		一.问题描述 今天一来公司,在IntelliJ IDEA 中启动Tomcat服务器时就出现了如下图所示的错误: 
- Java RMI 介绍和例子以及Spring对RMI支持的实际应用实例
		RMI 相关知识 RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网 ... 
- Java学习笔记(十六)——Java RMI
		[前面的话] 最近过的好舒服,每天过的感觉很充实,一些生活和工作的技巧注意了就会发现,其实生活也是可以过的如此的有滋有味,满足现在的状况,并且感觉很幸福. 学习java RMI的原因是最近在使用dub ... 
- JAVA RMI helloworld入门
		Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ... 
随机推荐
- UVA 10617 Again Palindrome 区间DP
			题目链接: https://cn.vjudge.net/problem/UVA-10617 题目大意: 问有几种删除字符的方法可以使得该字符串为回文. 解题思路: 删除字符得到回文串的方法数 等于 字 ... 
- 4、集合set的功能介绍
			集合是易变(可改变)和无序聚集.集合set支持迭代,很像无值(或仅有键的)字典,用花括号表示{}. 1.集合的创建: 可以通过调用内建函数set()来创建,及向其传递一个迭代,该迭代的项目成为形成 ... 
- 【ES6】最常用的es6特性(二)
			1.for of 值遍历 for in 循环用于遍历数组,类数组或对象,ES6中新引入的for of循环功能相似,不同的是每次循环它提供的不是序号而是值. var someArray = [ &quo ... 
- Spring(一)之概括与架构
			个人说明: 下面有一部分引用该链接:https://www.tutorialspoint.com/spring/spring_architecture.htm 另外一部分加上我个人的使用经验和体会 之 ... 
- java  常用类2
			1.1 日期时间类 时间戳(timestamp):距离特定时间的时间间隔. 计算机时间戳是指距离历元(1970-01-01 00:00:00:000)的时间间隔(ms). 计算机中时间2019-04- ... 
- Maven下使用Junit对Spring进行单元测试
			主要步骤 1. 在工程的pom文件中增加spring-test的依赖: <dependency> <groupId>org.springframework</groupI ... 
- Swift基础学习笔记 一
			之前学习过一段时间swift,由于目前开发的项目还是用的OC,一段时间不看swift又基本忘干净了,好记性不如烂笔头,还是用博客记录一下自己学的东西吧. 基本数据类型: 1.常量(let)和变量(va ... 
- vue面试题!!!
			由于公司需要,需要把项目拆分,前端使用vue框架.最近面试vue总结的试题 1:mvvm框架是什么?它和其他框架的区别是什么? mvvm 全称model view viewModel,model数据模 ... 
- Java并发编程(二)创建线程的三种方法
			进程与线程 1. 进程 进程和代码之间的关系就像音乐和乐谱之间的关系一样,演奏结束的时候音乐就不存在了但乐谱还在:程序执行结束的时候进程就消失了但代码还在,而计算机就是代码的演奏家. 2. 线程 线 ... 
- node引入bootstrap  npm报错
			今天node引入bootstrap npm报错 但是页面正常显示 最后发现bootstrap.min.js.map没有放在文件里 虽然不用页面中引入 另外也发现了怎么看这种错误了 
