Spring学习笔记之使用远程服务

1.Spring远程调用概览

远程调用是客户端应用和服务端应用之间的会话。在客户端,它所需要的一些功能并不在该应用的实现范围之内,所以应用要想能提供这些功能的·其他系统寻求帮助。而远程应用通过远程服务暴露这些功能。
RPC(remote-procedure call. RPC)远程过程调用:
就是执行流从一个应用传递给另一个应用,理论上另一个应用部署在跨网络的一台远程机器上。
Spring支持多种不同的RPC模型,暴露RMI,Caucho的Hessian和Burlap以及Spring自带的HTTP invoker。

RPC模型使用场景
远程方法调用(RMI)不考虑网络限制时(例如防火墙),访问/发布基于Java的服务
Hessian或Burlap考虑网络限制时,通过HTTP访问/发布基于Java的服务。Hessian是二进制协议.而Burlap是基于XML的
HTTP invoker考虑网络限制,并希望使用基于XML或专有的序列化机制实现Java序列化时,访问/发布基于Spring的服务
JAX-RPC和JAX-WS访问/fabu1平台独立的、基于SOAP的Web服务

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

要记住一点,任何传递给远程调用的bean或从远程调用返回的bean可能需要实现java.io.Serializable接口。

2.使用RMI

2.1导出RMI服务

如果你曾经创建过RMI服务,应该会知道这会这几如下几个步骤:

  1. 编写一个服务实现类,类中的方法必须抛出java.rmi.RemoteException异常;
  2. 创建一个继承于java.rmi.Remote的服务接口;
  3. 运行RMI编译器(rmic),创建客户端stub类和服务端skeleton类;
  4. 启动一个RMI注册表,以便持有这些服务;
  5. 在RMI注册表中注册服务。

spring提供了更简单的方式来发布RMI服务,不用再编写那些需要抛出RemoteException异常的特定RMI类,只需要简单地编写实现服务功能的POJO就可以了,Spring会处理剩余的事项。
下面是将要发布的服务接口:

import java.util.List;

import com.jpa.domin.User;

public interface RPCService {

    List<User> findAllQQEmailUser();
}

实现类:

@Service
public class RPCServiceImpl implements RPCService {

    @Autowired
    private ISpringDataJpaRepository sdjr;

    public List<User> findAllQQEmailUser() {
        return sdjr.findAllQQEmailUser();
    }

}

将它的实现类RPCServiceImpl发布为RMI服务:

@Bean
    @Autowired
    public RmiServiceExporter rmiServiceExporter(RPCService rpcService){
        RmiServiceExporter exporter = new RmiServiceExporter();
        exporter.setService(rpcService);
        exporter.setServiceName("RPCService");//命名RMI服务
        exporter.setServiceInterface(RPCService.class);//指定此服务所实现的接口
        return exporter;
    }

RmiServiceExporter 可以把任意Spring管理的bean发布为RMI服务。RmiServiceExporter 把bean包装在一个适配器类中,然后适配器类被绑定到RMI注册表中,并且代理到服务类的请求——在本例中服务类也就是RPCServiceImpl。

RmiServiceExporter把POJO包装到服务适配器中,并将服务适配器绑定到RMI注册表中,从而将POJO转换为RMI服务

默认情况下,RmiServiceExporter 会尝试绑定到本地机器1099端口上的RMI注册表。如果在这个端口没有发现RMI注册表,RmiServiceExporter 将会启动一个注册表。可以通过registryHost和registryPort属性来自定义:

@Bean
    @Autowired
    public RmiServiceExporter rmiServiceExporter(RPCService rpcService){
        RmiServiceExporter exporter = new RmiServiceExporter();
        exporter.setService(rpcService);
        exporter.setServiceName("RPCService");//命名RMI服务
        exporter.setServiceInterface(RPCService.class);//指定此服务所实现的接口
        exporter.setRegistryHost("rmi.test.com");//绑定到rmi.test.com主机
        exporter.setRegistryPort(1199);//绑定到1199端口上的注册表
        return exporter;
    }

2.2装配RMI服务

传统上,RMI客户端必须使用RMI API的Naming类从RMI注册表中查找服务。
而Spring则不需要这么麻烦,Spring的RmiProxyFactoryBean是一个工厂bean,该bean可以为RMI服务创建代理。使用RmiProxyFactoryBean引用RPCService的RMI服务是非常简单的:

@Bean
    public RmiProxyFactoryBean rPCService(){
        RmiProxyFactoryBean rmiProxy = new RmiProxyFactoryBean();
        rmiProxy.setServiceUrl("rmi://localhost/RPCService");//RPCService为RMI服务名
        rmiProxy.setServiceInterface(RPCService.class);
        return rmiProxy;
    }

下图展示了客户端和RMI的交互:
RmiProxyFactoryBean 生成一个代理对象,该对象代表客户端来负责与远程的RMI服务进行通信。客户端通过服务的接口与代理进行交互,就如同远程服务就是一个本地的POJO

现在已经把RMI服务声明为Spring管理的bean,我们就可以把它作为依赖装配进另一个bean中,就像任意非远程的bean那样使用了:

@Autowired
    private RPCService rPCService;
@RequestMapping("/me")
    public String me(){
        List<User> users = rPCService.findAllQQEmailUser();
        for(User u : users){
            System.out.println(u.getUsername());
        }
        return "me";
    }

需要注意的是,在这里的操作中,服务端和客户端都用到了同一个实体User,不仅要确保两个应用中的User一样,而且都实现了序列化,还要确保他们所在的包路径要一直,这是因为在编译class文件的时候,这个class文件内部已经包含了package com.cn;这样的内容,如果在部署的时候,不严格按照这个目录部署的话,服务器就会找不到,就会报no security manager: RMI class loader disabled异常。

RMI是一种实现远程服务交互的好办法,但是它存在某些限制。首先,RMI很难穿越防火墙,这是因为RMI使用任意端口来交互——这是防火墙通常所不允许的。其次,RMI是基于Java的,这意味这客户端和服务端必须都是用Java开发的。因为RMI使用Java的序列化机制,所以通过网络传输的对象类型必需要保证在调用两端的Java运行时中是完全相同的版本

3.使用Hessian和Burlap发布远程服务

Hessian和Burlap是Caucho Technology提供的两种基于HTTP的轻量级远程服务解决方案。借助于尽可能简单的API和通信协议,它们都致力于简化web服务。
Hessian,像RMI一样,使用二进制消息进行客户端和服务端的交互。但与其他二进制远程调用技术不同的是,它的二进制消息可以移植到其他非Java的语言中,包括PHP、Python、C++和C#。
Burlap是一种基于XML的远程调用技术,这使得它可以自然而然地移植到任何能够解析XML的语言上。正因为它基于XML,所以相比Hessian的二进制格式而言,Burlap可读性更强。但是和其他基于XML的远程技术不同,Burlap的消息结构尽可能的简单,不需要额外的外部定义语言。
他们之间的唯一区别在于Hessian的消息是二进制的,而Burlap的消息是XML。由于Hessian的消息是二进制的,所以它在带宽上更具优势。但是如果我们更注重可读性或者我们的应用需要与没有Hessian实现的语言交互,那么Burlap的XML消息会是更好的选择。

3.1使用Hessian和Burlap导出bean的功能

导出Hessian服务:
同RMI一样,这次只需要把RmiServiceExporter换成HessianServiceExporter

@Bean
    @Autowired
    public HessianServiceExporter hessianServiceExporter(RPCService rpcService){
        HessianServiceExporter serviceExporter = new HessianServiceExporter();
        serviceExporter.setService(rpcService);
        serviceExporter.setServiceInterface(RPCService.class);
        return serviceExporter;
    }

这里没有设置serviceName属性,在RMI中,该属性用来在RMI注册表中注册一个服务。而Hessian没有注册表,因此也就没有必要为Hessian服务进行命名。
下图是HessianServiceExporter 将POJO的public方法发布为Hessian服务的实现过程:
HessianServiceExporter 是一个Spring MVC控制器,他可以接收Hessian请求,并把这些请求转换成POJO的调用从而将POJO导出为一个Hessian服务

配置Hessian控制器:
由于Hessian是基于HTTP的,所以HessianServiceExporter 实现为一个Spring MVC控制器。这就意味这为了使用导出的Hessian服务,我们需要执行两个额外的配置步骤:

  • 在web.xml中配置Spring 的DispatcherServlet,并把我们的应用部署为Web应用
  • 在Spring的配置文件中配置一个URL处理器,把Hessian服务的URL分发给对应的Hessian服务bean。

这里我们使用Java配置:

public class MVCInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{

    @Override
    protected String[] getServletMappings() {
        // TODO Auto-generated method stub
        return new String[]{"/", "*.service"};//在原有的基础上加上*.service,是DispatcherServlet能够拦截后缀为*.service的URL
    }

}
@Bean 
    public HandlerMapping hessianMapping(){
        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        Properties mappings = new Properties();
        mappings.setProperty("/rpc.service", "hessianServiceExporter");
        mapping.setMappings(mappings);
        return mapping;
    }

hessianServiceExporter就是HessianServiceExporter 的bean name。

3.2访问Hessian/Burlap服务

和RMI一样,客户端我们只需要配置一个代理就可以了:

@Bean
    public HessianProxyFactoryBean rPCService(){
        HessianProxyFactoryBean proxy = new HessianProxyFactoryBean();
        proxy.setServiceUrl("http://localhost:8080/spring_jpa/rpc.service");
        proxy.setServiceInterface(RPCService.class);
        return proxy;
    }

如果使用Burlap,对应的服务端为:

@SuppressWarnings("deprecation")
    @Bean
    @Autowired
    public BurlapServiceExporter burlapServiceExporter(RPCService rpcService){
        BurlapServiceExporter serviceExporter = new BurlapServiceExporter();
        serviceExporter.setService(rpcService);
        serviceExporter.setServiceInterface(RPCService.class);
        return serviceExporter;
    }

客户端:

@Bean
    public BurlapProxyFactoryBean rPCService(){
        BurlapProxyFactoryBean proxy = new BurlapProxyFactoryBean();
        proxy.setServiceUrl("http://localhost:8080/spring_jpa/rpc.service");
        proxy.setServiceInterface(RPCService.class);
        return proxy;
    }

因为Hessian和Burlap都是基于HTTP的,它们都解决了RMI所头疼的防火墙渗透问题。但是当传递过来的RPC消息中包含序列化对象时,RMI就完胜Hessian和Burlap了。因为Hessian和Burlap都采用了私有的序列化机制,而RMI使用的是Java本身的序列化机制。如果我们的数据模型非常复杂,Hessian/Burlap的序列化模型就可能无法胜任了。

还有一个两全其美的方案,那就是Spring HTTP invoke,它基于HTTP(像Hessian和Burlap一样)提供了RPC,同时有使用了Java的对象序列化机制(像RMI一样)。

4.使用Spring的HttpInvoker

HTTP invoker是一个新的远程调用模型,作为Spring 框架的一部分,能够执行基于HTTP的远程调用(让防火墙不为难),并使用Java的序列化机制(让开发者也乐观其变)。
配置HTTP invoker和配置Hessian/Burlap过程是一样的,唯一的区别在于两个bean不一样,一个是导出bean(服务端),一个是代理bean(客户端)。
服务端:

@Bean
    @Autowired
    public HttpInvokerServiceExporter httpInvokerServiceExporter(RPCService rpcService){
        HttpInvokerServiceExporter serviceExporter = new HttpInvokerServiceExporter();
        serviceExporter.setService(rpcService);
        serviceExporter.setServiceInterface(RPCService.class);
        return serviceExporter;
    }

客户端:

@Bean
    public HttpInvokerProxyFactoryBean rPCService(){
        HttpInvokerProxyFactoryBean proxy = new HttpInvokerProxyFactoryBean();
        proxy.setServiceUrl("http://localhost:8080/spring_jpa/rpc.service");
        proxy.setServiceInterface(RPCService.class);
        return proxy;
    }

其他的比如配置DispatcherServlet的拦截请求,以及URL映射都是一样的。只需要把给定的URL映射到httpInvokerServiceExporter bean即可。

@Override
    protected String[] getServletMappings() {
        // TODO Auto-generated method stub
        return new String[]{"/", "*.service"};//在原有的基础上加上*.service,是DispatcherServlet能够拦截后缀为*.service的URL
    }
@Bean 
    public HandlerMapping hessianMapping(){
        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        Properties mappings = new Properties();
        mappings.setProperty("/rpc.service", "httpInvokerServiceExporter");
        mapping.setMappings(mappings);
        return mapping;
    }

需要记住的是HTTP Invoker有一个重大的限制:它只是一个Spring框架所提供的远程调用解决方案。者意味着客户端和服务端必须都是Spring应用。并且,至少目前而言,也隐含表明客户端和服务端必须是基于Java的。另外,因为使用了Java的序列化机制,客户端和服务端必须使用相同版本的类(与RMI类似)。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring为各种远程访问技术提供集成工具类。Spring远程访问通过使用普通POJOs,能更容易的开发远程访问服务。目前,Spring远程访问的主要技术如下: 1. 远程调用RMI(Remote Method Invocation): 通过使用 RmiProxyFactoryBean 和 RmiServiceExporter,并且,Spring支持两个传统的RMI使用 java.rmi.Remote接口和java.rmi.RemoteException)和通过RMI调用器实现的暴露远程调用(支持任何Java接口)。 2. Spring的HTTP调用器(Spring’s Http Invoker): Spring提供了一种特殊的允许通过HTTP进行Java串行化的远程调用策略,支持任意Java接口(就像RMI调用器)。相对应的支持类是 HttpInvokerProxyFactoryBean和 HttpInvokerServiceExporter。 3. Hessian: 通过 HessianProxyFactoryBean 和 HessianServiceExporter,可以使用Caucho提供的基于HTTP的轻量级二进制协议来透明地暴露服务。 4. Burlap: Burlap是Caucho的另外一个子项目,可以作为Hessian基于XML的替代方案。Spring提供了诸如 BurlapProxyFactoryBean 和 BurlapServiceExporter 的支持类。 5. JAX RPC: Spring通过JAX-RPC远程Web服务提供支持(J2EE 1.4's web service API)。 6. JAX-WS:Spring通过JAX-WS为远程Web服务提供支持(the successor of JAX-RPC, as introduced in Java EE 5 and Java 6)。 7. JMS:远程访问通过类JmsInvokerServiceExporter和JmsInvokerProxyFactoryBean使用JMS的底层协议实现。 二. 远程访问------RMI 1. RMI远程访问基本流程 1). 服务端定义远程访问接口; 2). 服务端通过RmiServiceExporter暴露服务接口 3). 客户端定义与服务端已暴露的相同接口 4). 客户端通过RmiProxyFactoryBean调用服务接口

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值