1 简介
Hessian 是 Caucho 公司开发的一种基于二进制 RPC 协议(Remote Procedure Call protocol)的轻量级远程调用框架,其使用简单的方法提供了 RMI 的功能。 相比 WebService,Hessian 更简单、快捷,它主要包括 Hessian 远程调用协议、Hessian 序列化协议和客户端服务端代理。特别提示,Hessian 远程调用框架是构建在 HTTP 协议之上的。
2 调用过程
其中,步骤 3、4、5、6 为核心过程,在此进行更深层次的解析,
步骤 3:将远程方法调用转换为 Hessian 调用,具体为,客户端首先要先和服务器端建立 Socket 连接,然后发送 HTTP 请求,Hessian 远程调用将经过 Hessian 序列化的参数等二进制数据作为 HTTP 请求的数据部分被提交到服务端,并且目前只支持 Post 提交方法。
步骤 4:将 Hessian 调用转换为本地方法调用,步骤 3 里请求的 url 是暴露服务时映射好的,也即指定的服务端代理对象会解析客户端服务代理对象进行的 Hessian 远程调用,然后反序列化参数,找到被代理的服务类(暴露服务时指定的服务类),通过反射调用服务类中的相应服务方法。
步骤 5:返回远程调用返回值给服务调用者,步骤 4 里调用服务类的方法会返回具体值,经过 Hessian 序列化后作为 Hessian 调用的返回值,被放在 HTTP 响应的 body 部分被返回给客户端。
步骤 6:客户端代理对象解析 body 部分 Hessian 调用返回 reply,解析出远程调用返回值再反序列化,最终得到结果。
3 注意事项
在进行基于 Hessian 协议的项目开发时,构建的服务端和客户端应该具备如下内容,
服务端:
包含 Hessian 的 jar 包;设计一个接口,用来给客户端调用;实现该接口的功能;配置web.xml
,配好相应的 Servlet;对于复杂对象可以使用 Map 的方法传递;由于使用二进制 RPC 协议传输数据,对象必须进行序列化,实现 Serializable 接口。
客户端:
包含 Hessian 的 jar 包;具有和服务器端结构一样的接口;利用 HessianProxyFactory 调用远程接口。
Hessian 的 jar 包可以通过下面提供的两个地址下载:
方法1 ->hessian-4.0.37(最新版本的jar包)方法2 ->hessian官方网站
4 调用示例
4.1 示例一
新建一个 Web Project 项目,将 Hessian 的 jar 包导入到External Libraries
中:
/*** 创建接口*/package com.hit;public interface BasicAPI {public void setGreeting(String greeting);public String hello();public User getUser();}
/*** 实现接口*/package com.hit;public class BasicService implements BasicAPI {private String _greeting = "Hello, hessian";public void setGreeting(String greeting) {_greeting = greeting;System.out.println("Set greeting success:" + _greeting);}public String hello(){return _greeting;}public User getUser() {return new User("charies", "xiaomima");}}
/*** 创建一个实现 Serializable 接口的 POJO 类(简单的 Java 类),也可以说是 Bean*/package com.hit;import java.io.Serializable;public class User implements Serializable {String userName = "charies";String password = "xiaomima";public User(String user, String pwd) {this.userName = user;this.password = pwd;}public String getUserName() {return userName;}public String getPassword() {return password;}}
<!-- 配置 web.xml --><?xml version="1.0" encoding="UTF-8"?><web-app xmlns="/xml/ns/javaee"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/xml/ns/javaee /xml/ns/javaee/web-app_3_1.xsd"version="3.1"><display-name>Hessian</display-name><servlet><servlet-name>helloHssian</servlet-name><servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class><init-param><param-name>service-class</param-name><param-value>com.hit.BasicService</param-value></init-param></servlet><servlet-mapping><servlet-name>helloHssian</servlet-name><url-pattern>/helloHssian</url-pattern></servlet-mapping></web-app>
接下来,再创建一个 Java Project,导入 Hessian 的 jar 包,并创建与服务器端一样的接口及基础类:
package com.hit;public interface BasicAPI{public void setGreeting(String greeting);public String hello();public User getUser();}
package com.hit;import java.io.Serializable;public class User implements Serializable{String userName ="charies";String password ="xiaomima";public User(String user, String pwd) {this.userName = user;this.password = pwd;}public String getUserName() {return userName;}public String getPassword() {return password;}}
package com.hit;import com.caucho.hessian.client.HessianProxyFactory;public class BasicClient {public static void main(String[] args) throws Exception {String url ="http://localhost:8080/Hessian/helloHessian";HessianProxyFactory factory = new HessianProxyFactory();BasicAPI basic = (BasicAPI) factory.create(BasicAPI.class, url);System.out.println("Hello:" + basic.hello());System.out.println("Hello:" + basic.getUser().getUserName());System.out.println("Hello:" + basic.getUser().getPassword());basic.setGreeting("HelloGreeting");System.out.println("Hello:" + basic.hello());}}
4.2 示例二
创建服务端:
/*** 创建接口*/package com.hit.hessian.service;public interface Basic {public String sayHello();}
/*** 实现接口*/package com.hit.hessian.service;public class BasicService implements Basic {private String message = "We are champion!";@Overridepublic String sayHello() {return message;}}
/*** 实现接口并继承 HessianServlet*/package com.hit2.hessian.service;import com.caucho.hessian.server.HessianServlet;public class BasicServiceAlso extends HessianServlet implements Basic{private String message = "you you, qie ke nao!";@Overridepublic String sayHello() {return message;}}
<!-- 配置 web.xml --><?xml version="1.0" encoding="UTF-8"?><web-app xmlns="/xml/ns/javaee"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/xml/ns/javaee /xml/ns/javaee/web-app_3_1.xsd"version="3.1"><display-name>HessianWeb</display-name><servlet><servlet-name>hello</servlet-name><servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class><init-param><param-name>home-class</param-name><param-value>com.hit.hessian.service.BasicService</param-value></init-param><init-param><param-name>home-api</param-name><param-value>com.hit.hessian.service.Basic</param-value></init-param></servlet><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/helloHessian</url-pattern></servlet-mapping><servlet><servlet-name>helloAlso</servlet-name><servlet-class>com.hit.hessian.service.BasicServiceAlso</servlet-class> </servlet><servlet-mapping><servlet-name>helloAlso</servlet-name><url-pattern>/helloHessianAlso</url-pattern></servlet-mapping></web-app>
在服务端端,我们定义了两个 Service,分别为 BasicService 和 BasicServiceAlso,不同之处在于是否继承 HessianServlet,BasicService 是一个 POJO(简单的 Java 类),而 BasicServiceAlso 则是一个 Servlet. 在web.xml
里,BasicService 是通过home-class
和home-api
两个参数传递给 HessianServlet,然后再将 HessianServlet 配置到web.xml
的 Servlet 里来实现服务配置到容器的。而 BasicServiceAlso ,则是直接将自己(它自己就是个 Servlet)配置到web.xml
的 Servlet 来实现配置到容器中的。如果我们在一个应用中要实现多个 Hessian 服务,应该采用这种方式。
创建客户端:
/*** 创建与服务器端相同的接口*/package com.hit.hessian.service;public interface Basic {public String sayHello();}
/** 创建测试客户端*/package com.hit.hessian.client;import .MalformedURLException;import com.caucho.hessian.client.HessianProxyFactory;import com.hit.hessian.service.Basic;public class HessianClient {public static void main(String[] args) throws MalformedURLException {//String url = "http://localhost:8080/HessianWeb/helloHessian";String url = "http://localhost:8080/HessianWeb/helloHessianAlso";HessianProxyFactory factory = new HessianProxyFactory();Basic basic = (Basic)factory.create(Basic.class, url);System.out.println(basic.sayHello());}}
客户端要定义一个同服务器端一模一样的接口,然后通过 HessianProxyFactory 获得代理,并调用远程服务的方法。注意:这里笔者故意将客户端与服务器端的 Basic 接口的包定义成不同的路径(一个是 com.hit.hessian.service,一个是 com.hit2.hessian.service),经过验证这样是可以的,但是推荐最好两者一模一样。
4.3 常见的异常及错误的解决方案
在运行以上的代码时,一不小心就会报错,常见的异常及错误的解决方法有:
(1)在启动 tomcat 的时候,出现如下问题java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
,而在工程中是可以找到相应的 jar 文件,怎么办?
解决方法:可能是在工程的WEB-INF/lib
下面没有加载相应的 jar 文件 。
(2)控制台显示org.springframework.remoting.RemoteAccessException: Cannot access Hessian service at XXX
这个异常。
解决方法:出现这个异常一般是因为服务端操作出现异常引起的,需要重新检查代码。
(3)编译时错误无法访问 javax.servlet.http.HttpServlet ; 未找到 javax.servlet.http.HttpServlet 的类文件。
解决方法:可能是环境变量没有配置,或者就是根本没有包含该类的jar包,可以参考 「出现 javax.servlet.http.HttpServlet 错误的原因及解决方法」进行解决。
(4)在 WEB-INF 目录下加载完 Hessian 的 jar 包后,代码仍然报错,怎么办?
解决方法:出现这个情况的时候,我们可以尝试着把 Hessian 的 jar 包再加载到External Libraries
里面。
(5)在通过 Hessian 协议进行远程服务调用的过程中,代码总是报错,异常信息提示为HessianConnectionException
,如何解决?
解决方法:出现这种异常的原因有可能是接口的实现类没有进行注入,即通过@component("接口名称,第一个字母小写")
注解进行注入,也有可能是代码没有提交到服务器,从而导致代码没有生效。
参考资料:
[1] Hessian - 百度百科
[2] Hessian 轻量级远程调用方案
[3] Hessian 远程接口调用原理
[4] Hessian 轻量级二进制远程调用框架
[5] Hessian 学习
[6] Hessian 简单实用