文章目录
前言
在很久之前, 我们一起领略了 RMI 提供远程服务的过程
https://blog.csdn.net/bestcxx/article/details/79488165
本篇我们继续深入下去,了解 Hessian 提供远程服务的过程
相比 RMI 而言,Hessian 是跨语言的
RMI 是 Java 语言自己提供的远程调用过程解决方法,它要求服务的提供者和调用者都必须是 Java 语言,而由于 Hessian 对外而言类似于一个 http 服务,所以可以跨语言访问。延伸来说,由于 RMI 需要注册端口和一个随机分配的数据通信端口,导致其会遇到防火墙问题,而 Hessian 借助于 http 服务,可以避免这个问题。
Hessian 基于二进制消息通信
和 RMI 一样,Hessian 也是通过二进制消息进行通信的。
序列化的问题
Hessian 的序列化机制不同于 Java 的序列化机制,Hessian 的序列化机制相对而言更加快捷,但是可靠性不如java,因为 Hessian 的序列化机制是私有的,所以造成其序列化信息并没有携带全部的信息,这导致在处理复杂对象的时候有时会出现问题。
Spring 4 结合 Hessian 4
Maven 依赖引入 Hessian 包-服务端和客户端
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.38</version>
</dependency>
服务端的特殊配置
- 类似于 RMI 服务的发布,Hessian 服务的发布也需要先将本地的服务声明为 Hessian 服务
/**
* 注册为远程服务
* @return
*/
@Bean
public HessianServiceExporter hessianExportedApiService(){
HessianServiceExporter exporter=new HessianServiceExporter();
exporter.setService(getApiService());
exporter.setServiceInterface(ApiService.class);
return exporter;
}
/**
* 注册为Spring Bean
* @return
*/
@Bean
public ApiService getApiService(){
return new ApiServiceImpl();
}
- 除此之外,还需要设置一个 url 映射,当 web 访问请求符合映射时,将会转由 Hessian 服务来进行处理
/**
* 设置URL映射
*/
@Bean
public HandlerMapping hessianMapping(){
SimpleUrlHandlerMapping shm=new SimpleUrlHandlerMapping();
Properties properties=new Properties();
properties.put("/api.service", "hessianExportedApiService");
shm.setMappings(properties);
return shm;
}
- 最后服务端是一个web工程,所以附带web.xml 的内容
<servlet-mapping>
<servlet-name>springservlet</servlet-name>
<url-pattern>/</url-pattern>
<!-- <url-pattern>*.service</url-pattern> -->
</servlet-mapping>
客户端的调用
客户端的调用过程相对服务端简单了许多
/**
* 为远程服务设置代理
* @return
*/
@Bean
public HessianProxyFactoryBean apiService(){
HessianProxyFactoryBean proxy=new HessianProxyFactoryBean();
proxy.setServiceUrl("http://localhost:8086/hessian-provider/api.service");
proxy.setServiceInterface(ApiService.class);
return proxy;
}
- github 完整例子
https://github.com/Bestcxy/RPC/tree/master/hessian
hessian-api 定义接口
hessian-provider 提供服务
hessian-consumer 调用Hessian 服务
- 调用结果
启动两个web 项目,访问 http://localhost:8085/hessian-consumer/
{"result":"success","name":"服务端:用户输入了姓名=jecket","model":{"userName":"jecket","age":20}}
关于 Hessian 远程服务的测试
当 Hessian 服务启动之后,我们可以通过 Spring 的测试体系对接口进行测试(可以参考 Junit 使用集合),也可以使用 HessianProxyFactory 直接对远程服务进行测试,而且后者会更加简化。这个理由给出 HessianProxyFactory 测试远程接口的方法
package com.bestcxx.stu.rpc.hessian.consumer;
import java.net.MalformedURLException;
import org.junit.Test;
import com.bestcxx.stu.rpc.hessian.api.service.ApiService;
import com.caucho.hessian.client.HessianProxyFactory;
/**
* 使用 HessianProxyFactory 直接测试远程接口的调用
* @author Jecket
*
*/
public class ApiServiceTest {
@Test
public void testGetName(){
HessianProxyFactory hessianProxyFactory=new HessianProxyFactory();
ApiService apiService;
try {
apiService=(ApiService) hessianProxyFactory.create(ApiService.class, "http://localhost:8086/hessian-provider/api.service");
System.out.println(apiService.getName("Jecket"));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
一个邪门的问题:客户端可以独立写api接口调用服务吗?
标题还不太完整,我再具体阐述一下。
本文的例子中,服务端和客户端都依赖 hessian-api项目,该项目定义了接口和抽象方法,实体等信息。
hessian-provider 提供接口的实现,并发布为 hessian服务,hessian-consumer 通过 hessian-api 中提供的接口和实体来获取 hessian 服务,问,加入hessian-conusmer不依赖 hessian-api,而是在自己的项目中写一套其原本依赖的 接口和实体等,可不可行?
经过实验发现,是可以的,并且这种情况 Model 的序列号不同也不会有影响。
这样做的坏处很明显,就是客户端和服务端的api分离了,加入服务端变更,客户端不知道,但是可以作为一种知识扩充——Java 用包路径+类名+类加载器来唯一限定一个类。