hessian 是一款開源的二進制遠程通訊協議,使用簡單方法提供了RMI功能,主要用於面向對象的消息通信。 優點:跨平台、多語言支持、使用簡單 缺點:傳遞復雜對象性能會下降,不適合安全性高的應用
一 、hessian demo 示例:
1、新建一個maven項目,包含3個模塊 API 模塊(遠程接口,供客戶端和服務端使用),客戶端模塊和服務端模塊
2、添加hessian依賴包
在pom.xml 中添加hessian依賴
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.38</version>
</dependency>
3、遠程接口定義
在hessian-api 模塊定義接口
public interface HelloHessian {
String sayHello(User user);
}
4、服務端接口實現
在hessian-server 模塊實現API 接口
public class HelloHessianImpl implements HelloHessian {
public String sayHello(User user) {
String userName = user.getName();
System.out.println("hello:"+userName);
return userName;
}
}
5、服務端配置遠程服務地址
hessian 是依賴web 容器 需在web.xml 中配置服務請求地址
<servlet>
<servlet-name>HelloHessian</servlet-name>
<!-- RPC HessianServlet處理類 -->
<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
<init-param>
<param-name>service-class</param-name>
<!-- 遠程服務實現類 -->
<param-value>com.hessian.server.HelloHessianImpl</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloHessian</servlet-name>
<url-pattern>/HelloHessian</url-pattern>
</servlet-mapping>
6、客戶端遠程接口調用
// 遠程服務地址
String url = "http://localhost:8080/HelloHessian";
//通過hessian代理工程創建接口代理
HessianProxyFactory fatory = new HessianProxyFactory();
HelloHessian helloHessian = (HelloHessian)fatory .create(HelloHessian.class,url);
// 執行方法調用
helloHessian.sayHello(new User(10, "tiger"));
7、運行demo
7.1、啟動服務器
7.2、運行客戶端程序
控制台打印 hello:tiger
二、hessian 工作流程
三、客戶端源碼分析
1、客戶端請求時序圖
2、客戶端創建接口的代理類分析:
HessianProxyFactory factory = new HessianProxyFactory();
HelloHessian helloHessian = (HelloHessian)factory.create(HelloHessian.class,url)
helloHessian.sayHello(new User(10, "tiger"))
2.1 通過HessianProxyFactory hessian代理工程類 創建代理類HessianProxy 具體代碼如下:
public Object create(Class<?> api, URL url, ClassLoader loader) {
......
InvocationHandler handler = new HessianProxy(url, this, api);
return Proxy.newProxyInstance(loader, new Class[] { api, HessianRemoteObject.class }, handler);
}
2.2 HessianProxy 源碼分析:
代理類HessianProxy 實現了InvocationHandler 接口 invoke(Object proxy, Method method, Object[] args)
2.2.1 獲取調用的方法名以及方法參數類型
String methodName = method.getName();
2.2.2 equals、hashCode、getHessianType、getHessianURL、toString 本地調用
if(is1.equals("hashCode") && conn.length == 0) {
return new Integer(this._url.hashCode());
}
if(is1.equals("getHessianType")) {
return proxy.getClass().getInterfaces()[0].getName();
}
2.2.3 重載支持 重載處理 mangleName = sayHello_User // 方法名_參數1_參數2_XX
2.2.4 執行遠程調用(建立http鏈接,設置http header 信息, 序列化方法和參數,執行http請求)
//建立http鏈接
URLConnection conn = this._factory.openConnection(this._url);
//設置http header 信息
this.addRequestHeaders(conn);
//序列化並輸出流
AbstractHessianOutput out = this._factory.getHessianOutput(conn.getOutputStream());
out .call(mangleName , args) // out .call("sayHello",new Object[]{user})
out .flush();
call 方法封裝hessian 版本信息、調用的方法名稱、參數信息,格式如下
'c' 一個調用方法編碼的開始標識符
1和0 Hessian的版本號,各占1 byte
length 方法名稱的長度,占2 byte
method_name 方法名稱
[params] 參數序列化內容,此參數是可選的
'z' 一個調用方法編碼的結束標識符
eg:
User user = new User(10,"tiger");
FileOutputStream fos = new FileOutputStream("out.txt");
HessianOutput out = new HessianOutput(fos);
out.call("sayHello",new Object[]{user});
用BinaryViewer 工具打開 out.txt ,查看內容:此處為16進制數表示
63 01 00 6D 00 08 73 61 79 48 65 6C 6C 6F 4D 74
00 07 76 6F 2E 55 73 65 72 53 00 03 61 67 65 49
00 00 00 0A 53 00 04 6E 61 6D 65 53 00 05 74 69
67 65 72 7A 7A
63為'c'字符的編碼
01 00為Hessian協議版本
6D為'm'字符編碼
00 08 為函數名稱的長度,因為'sayHello’函數的長度為8
73 61 79 48 65 6C 6C 6F 即為'sayHello'函數名稱的字符編碼;
7A為'z'的字符編碼
2.2.5 獲取輸入流並反序列
is = conn.getInputStream();
AbstractHessianInput in = _factory.getHessianInput(is);
Object value = in.readObject(method.getReturnType())