基于Spring的RPC通讯模型.
一、概念和原理
RPC(remote procedure call),远程过程调用,是客户端应用和服务端之间的会话。在客户端,它所需要的一些功能并不在该应用的实现范围之内,所以应用要向提供这些功能的其他系统寻求帮助。而远程应用通过远程服务暴露这些功能。RPC 是同步操作,会阻塞调用代码的执行,直到被调用的过程执行完毕。
Spring支持多种不同的RPC模型,包括RMI、Caucho的Hessian和Burlap以及Spring自带的HTTP invoker:

客户端:
在所有的模型中,服务都是作为 Spring 所管理的 bean 配置到我们的应用中。这是通过一个代理工厂 bean 实现的,这个bean能够把远程服务像本地对象一样装配到其他bean的属性中。
客户端向代理发起调用,就像代理提供了这些服务一样。代理代表客户端和远程服务进行通信,由它负责处理连接的细节并向远程服务发起调用。

服务端:
Spring 使用远程导出器(remote exporter)将bean方法发布为远程服务。

二、RMI
RMI 最初在JDK 1.1被引入到Java平台中,它为Java开发者提供了一种强大的方法来实现Java程序间的交互。
Spring 提供了简单的方式来发布RMI服务,在服务端,RmiServiceExporter 可以把任何 Spring 管理的bean发布为RMI服务 ,如图所示,RmiServiceExporter 把bean包装在一个适配器类中,然后适配器类被绑定到RMI注册表中,并且代理到服务类的请求。

/**
* 服务端:
* <p>
* 1、默认情况下,RmiServiceExporter 会尝试绑定到本地机器1099端口上的RMI注册表。
* 2、如果在这个端口没有发现RMI注册表,RmiServiceExporter 将会启动一个注册表。
* 3、可重写注册表的路径和端口,这个是个大坑,当你设置了registryHost属性的时候,源码中就不创建Registry,而是直接去获取,可是我们自己也没有创建,所以就会报连接不上。
*
* @param userService
* @return
*/
@Bean(name = "rmiServiceExporter")
public RmiExporter rmiServiceExporter(UserService userService, Environment environment) {
String registryHost = environment.getProperty("registryHost");
int registryPort = environment.getProperty("registryPort", Integer.class);
RmiExporter rmiExporter = new RmiExporter();
rmiExporter.setService(userService); //要把该bean(即rmiServiceImpl)发布为一个RMI服务
rmiExporter.setServiceName("RmiService"); //命名RMI 服务
rmiExporter.setServiceInterface(UserService.class); //指定服务所实现的接口
rmiExporter.setRegistryHost(registryHost);
rmiExporter.setRegistryPort(registryPort);
return rmiExporter;
}
/**
* Created by XiuYin.Cui on 2018/5/14.
*
* 解决设置 registryHost 后,报连接拒绝的问题。
*/
public class RmiExporter extends RmiServiceExporter { @Override
protected Registry getRegistry(String registryHost, int registryPort, RMIClientSocketFactory clientSocketFactory,
RMIServerSocketFactory serverSocketFactory) throws RemoteException { if (registryHost != null) {
try {
if (logger.isInfoEnabled()) {
logger.info("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]");
}
//把spring源代码中这里try起来,报异常就创建一个
Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory);
testRegistry(reg);
return reg;
} catch (RemoteException ex) {
LocateRegistry.createRegistry(registryPort);
Registry reg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory);
testRegistry(reg);
return reg;
}
} else {
return getRegistry(registryPort, clientSocketFactory, serverSocketFactory);
}
}
}
接下来,来看看客户端是怎么使用这些远程服务的吧!Spring的RmiProxyFactoryBean是一个工厂bean,该bean可以为RMI服务创建代理。该代理代表客户端来负责与远程的RMI服务进行通信。客户端通过服务的接口与代理进行交互,就如同远程服务就是一个本地的POJO。

@Bean(name = "rmiUserServiceClient")
public RmiProxyFactoryBean RmiUserServiceClient(){
RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
rmiProxyFactoryBean.setServiceUrl("rmi://127.0.0.1:9999/RmiService");
rmiProxyFactoryBean.setServiceInterface(UserService.class);
rmiProxyFactoryBean.setLookupStubOnStartup(false);//不在容器启动后创建与Server端的连接
rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);//连接出错的时候自动重连
rmiProxyFactoryBean.afterPropertiesSet();
return rmiProxyFactoryBean;
}
@Resource(name="rmiUserServiceClient")
private UserService userService;
RMI 的缺陷:
1、RMI很难穿越防火墙,这是因为RMI使用任意端口来交互——这是防火墙通常所不允许的。
2、RMI是基于Java的。这意味着客户端和服务端必须都是用java开发。因为RMI使用了Java的序列化机制,所以通过网络传输的对象类型必须要保证在调用两端的Java运行时中是完全相同的版本。
tips:最近发现 Dubbo 底层也是用 RMI 实现的,它把 zookeeper 当作注册表。
三、Hessian 和 Burlap
hessian 和 Burlap 是 Caucho Technology 的两种基于HTTP的轻量级远程服务解决方案。借助于尽可能简单的API和通信协议,它们都致力于简化Web服务。
hessian,像RMI一样,使用二进制消息进行客户端和服务端的交互。但是它与RMI不同的是,它的二进制消息可以移植到其他非Java的语言中。由于它是基于二进制的,所以它在带宽上更具优势。
Burlap 是一种基于XML的远程调用技术,这使得它可以自然而然的移植到任何能够解析XML的语言上。正因为它基于XML,所以相比起Hessian的二进制格式而言,Burlap可读性更强。但是和其他基于XML的远程技术(例如SOAP或XML-RPC)不同,Burlap的消息结构尽可能的简单。
下面我们会介绍 hessian 的使用。Spring 不推荐使用 Burlap,BurlapServiceExporter 在4.0后被废弃,不再提供支持。5.0 后直接从开发包丢弃了。
服务端,类似于 RmiServiceExporter ,hessian 也有一个 HessianServiceExporter 将 Spring 管理的 bean 发布为 Hessian 服务,不同于RMI的是,HessianServiceExporter是一个Spring MVC控制器,它接收Hessian请求(HTTP协议的请求),并将这些请求转换成对被导出POJO的方法调用。既然是HTTP请求,那我们就必须配置Spring 的 DispatcherServlet ,并配置 HandlerMapping,将相应的URL映射给 HessianServiceExporter。

/**
* hessian没有注册表,不需要设置 serviceName
*/
@Bean(name = "hessianServiceExporter")
public HessianServiceExporter hessianServiceExporter(UserService userService) {
HessianServiceExporter hessianServiceExporter = new HessianServiceExporter();
hessianServiceExporter.setService(userService);
hessianServiceExporter.setServiceInterface(UserService.class);
return hessianServiceExporter;
}
/**
* 需要配置一个URL映射来确保DispatcherServlet把请求转给HessianServiceExporter
*/
@Bean(name = "handlerMapping")
public HandlerMapping handlerMapping() {
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
Properties mappings = new Properties();
mappings.setProperty("/user.service", "hessianServiceExporter");
handlerMapping.setMappings(mappings);
return handlerMapping;
}
客户端,类似于 RmiProxyFactoryBean ,Hessian 也有一个代理工厂Bean——HessianProxyFactoryBean,来创建代理与远程服务进行通信:
@Bean(name = "hessianUserServiceClient")
public HessianProxyFactoryBean hessianUserServiceClient(){
HessianProxyFactoryBean proxy = new HessianProxyFactoryBean();
proxy.setServiceUrl("http://127.0.0.1:8080/user.service");
proxy.setServiceInterface(UserService.class);
return proxy;
}
@Resource(name="hessianUserServiceClient")
private UserService userService;
Hessian 的缺陷:
hessian 和 Burlap 都是基于HTTP的,它们都解决了RMI所头疼的防火墙渗透问题。但是当传递过来的RPC消息中包含序列化对象时,RMI就完胜 Hessian 和 Burlap 了。因为 Hessian 和 Burlap 都采用了私有的序列化机制,而RMI使用的是Java本身的序列化机制。
四、HttpInvoker
RMI 和 Hessian 各有自己的缺陷,一方面,RMI使用Java标准的对象序列化机制,但是很难穿透防火墙。另一方面,Hessian和Burlap能很好地穿透防火墙,但是使用私有的对象序列化机制。就这样,Spring的HTTP invoker应运而生了。HTTP invoker是一个新的远程调用模型,作为Spring框架的一部分,能够执行基于HTTP的远程调用,并使用Java的序列化机制。
HttpInvoker 的使用和 Hessian 很类似,HttpInvokerServiceExporter 也是一个Spring MVC 控制器,也是通过 DispatcherServlet 将请求分发给它...

/*Http Invoker*/
@Bean(name = "httpInvokerServiceExporter")
public HttpInvokerServiceExporter httpInvokerServiceExporter(UserService userService){
HttpInvokerServiceExporter httpInvokerServiceExporter = new HttpInvokerServiceExporter();
httpInvokerServiceExporter.setService(userService);
httpInvokerServiceExporter.setServiceInterface(UserService.class);
return httpInvokerServiceExporter;
}
/**
* 需要配置一个URL映射来确保DispatcherServlet把请求转给HessianServiceExporter
*/
@Bean(name = "handlerMapping")
public HandlerMapping handlerMapping() {
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
Properties mappings = new Properties();
mappings.setProperty("/user.service", "hessianServiceExporter");
mappings.setProperty("/userInvoker.service", "httpInvokerServiceExporter");
handlerMapping.setMappings(mappings);
return handlerMapping;
}
客户端,像 RmiProxyFactoryBean 和 HessianProxyFactoryBean 一样,HttpInvoker 也提供了一个代理工厂Bean——HttpInvokerProxyFactoryBean,用于创建HttpInvoker代理来与远程服务通信:
@Bean(name = "httpInvokerUserServiceClient")
public HttpInvokerProxyFactoryBean httpInvokerUserServiceClient(){
HttpInvokerProxyFactoryBean proxy = new HttpInvokerProxyFactoryBean();
proxy.setServiceUrl("http://127.0.0.1:8080//userInvoker.service");
proxy.setServiceInterface(UserService.class);
return proxy;
}
@Resource(name="httpInvokerUserServiceClient")
private UserService userService;
参考资料:《Spring 实战第四版》
演示源代码链接:https://github.com/JMCuixy/SpringForRpc
基于Spring的RPC通讯模型.的更多相关文章
- 基于netty实现rpc框架-spring boot服务端
demo地址 https://gitee.com/syher/grave-netty RPC介绍 首先了解一下RPC:远程过程调用.简单点说就是本地应用可以调用远程服务器的接口.那么通过什么方式调用远 ...
- 带你手写基于 Spring 的可插拔式 RPC 框架(一)介绍
概述 首先这篇文章是要带大家来实现一个框架,听到框架大家可能会觉得非常高大上,其实这和我们平时写业务员代码没什么区别,但是框架是要给别人使用的,所以我们要换位思考,怎么才能让别人用着舒服,怎么样才能让 ...
- 带你手写基于 Spring 的可插拔式 RPC 框架(二)整体结构
前言 上一篇文章中我们已经知道了什么是 RPC 框架和为什么要做一个 RPC 框架了,这一章我们来从宏观上分析,怎么来实现一个 RPC 框架,这个框架都有那些模块以及这些模块的作用. 总体设计 在我们 ...
- 基于Netty打造RPC服务器设计经验谈
自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...
- 这样基于Netty重构RPC框架你不可能知道
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365天原创计划”第5天. 今天呢!灯塔君跟大家讲: 基于Netty重构RPC框架 一.CyclicBarrier方法说明 1. ...
- 基于Spring Boot和Spring Cloud实现微服务架构学习
转载自:http://blog.csdn.net/enweitech/article/details/52582918 看了几周Spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习感 ...
- 基于Spring Boot和Spring Cloud实现微服务架构学习--转
原文地址:http://blog.csdn.net/enweitech/article/details/52582918 看了几周spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习 ...
- 基于Spring Boot+Cloud构建微云架构
前言 首先,最想说的是,当你要学习一套最新的技术时,官网的英文文档是学习的最佳渠道.因为网上流传的多数资料是官网翻译而来,很多描述的重点也都偏向于作者自身碰到的问题,这样就很容易让你理解和操作出现偏差 ...
- Spring实战5:基于Spring构建Web应用
主要内容 将web请求映射到Spring控制器 绑定form参数 验证表单提交的参数 对于很多Java程序员来说,他们的主要工作就是开发Web应用,如果你也在做这样的工作,那么你一定会了解到构建这类系 ...
随机推荐
- 某公司基于FineBI数据决策平台的试运行分析报告
一.数据平台的软硬件环境 二.组织机构和权限体系 组织机构:平台中已集成一套组织机构,可以建立部门.人员.也可以与现有系统的组织机构集成,将组织机构导入到平台中. 功能权限:通过配置功能点URL的方式 ...
- i++是否原子操作?并解释为什么???????
不是原子操作.理由: 1.i++分为三个阶段: 内存到寄存器 寄存器自增 写回内存 这三个阶段中间都可以被中断分离开. 2.++i首先要看编译器是怎么编译的, 某些编译器比如VC在非优化版本中会编译 ...
- GROUP BY,WHERE,HAVING间的区别和用法
having子句与where都是过滤语句. where 子句的作用是在对查询结果进行分组前,将不符合where条件的行去掉,即在分组之前过滤数据,条件中不能包含聚组函数,使用where条件显示特定的行 ...
- Linux进程管理(第二版) --进程管理命令
进程管理命令 一.查看用户信息.5.15 分钟内的系统的,优先值越小,优先权越大 ] 1.nice 指定程序运行的优先级 格式 nice -n command 例如 nice -5 myprogrem ...
- Leetcode_204_Count Primes
本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/46366207 Description: Count the ...
- android wheelview实现三级城市选择
很早之前看淘宝就有了ios那种的城市选择控件,当时也看到网友有分享,不过那个写的很烂,后来(大概是去年吧),我们公司有这么一个项目,当时用的还是网上比较流行的那个黑框的那个,感觉特别的丑,然后我在那个 ...
- Android高效率编码-第三方SDK详解系列(一)——百度地图,绘制,覆盖物,导航,定位,细腻分解!
Android高效率编码-第三方SDK详解系列(一)--百度地图,绘制,覆盖物,导航,定位,细腻分解! 这是一个系列,但是我也不确定具体会更新多少期,最近很忙,主要还是效率的问题,所以一些有效的东西还 ...
- 【Qt编程】3D迷宫游戏
说起迷宫想必大家都很熟悉,个人感觉迷宫对人的方向感是很大的考验,至少我的方向感是不好的,尤其是在三维空间中.由于这段时间帮导师做项目用到了三维作图,便心血来潮想做个三维迷宫玩玩.要想画出三维的迷宫游戏 ...
- MakeFile 文件的作用
makefile文件保存了编译器和连接器的参数选项,还表述了所有源文件之间的关系(源代码文件需要的特定的包含文件,可执行文件要求包含的目标文件模块及库等).创建程序(make程序)首先读取makefi ...
- Oracle12c(12.1)中性能优化&功能增强之通过参数THREADED_EXECTION使用多线程模型
1. 后台 UNIX/Linux系统上,oracle用多进程模型.例如:linux上一个常规安装的数据库会有如下进程列: $ ps -ef | grep [o]ra_ oracle 15356 ...