遠端通訊框架-Hessian
一、什麼是hessian
hessian 是一個基於binary-RPC 實現的遠端通訊library。
使用二進位制傳輸資料。
hessian 通常通過Web應用來提供服務,通過介面暴露。
Servlet 和 Spring的DispatcherServlet都可以把請求轉發給Hessian服務。
由以下兩種方式提供:
com.caucho.hessian.server.HessianServlet。
org.springframework.web.servlet.DispatcherServlet。
關於hessian的問題:
1、是基於什麼協議實現的
基於Binary-RPC協議實現的。
2、怎麼發起請求?
通過hessian本身提供的API來發起請求。
3、怎樣將請求轉化為符合協議格式的?
hessian通過其自定義的序列化機制將請求資訊進行序列化,產生二進位制流。
4、使用什麼傳輸協議傳輸?
hessian基於Http協議進行傳輸。
5、響應端基於什麼機制來接收請求?
響應端根據hessian提供的API來接收請求。
6、怎麼講流還原為傳輸格式?
hessian根據其私有的序列化機制來將請求資訊進行反序列化,傳遞給使用者 時已是相應的請求資訊物件了。
7、處理完畢後怎麼迴應?
處理完畢後直接返回,hessian將結果物件進行序列化,傳輸至呼叫端。
二、Hessian的優缺點
缺點:
缺乏安全機制,傳輸沒有加密
異常機制不完善,總是報一些錯誤,錯誤原因也是很多,提示資訊不足。
事務處理欠缺
版本問題,(我自己在專案中使用的是hessian-3.1.3.jar)
優點:
簡單易用,面向介面,通過介面暴露服務,不需要配置防火牆效率高,複雜物件序列化速度僅次於RMI,簡單物件序列化優於RMI,二進位制傳輸多語言支援:wiki、java、Flash/Flex、Python、C++、.NET C# 、PHP等
可與Spring整合,配置簡單
三、各個通訊協議對比: 通訊效率測試結果: RMI > Httpinvoker >= Hessian >> Burlap >> Web service 1.RMI 是 Java 首選遠端呼叫協議,非常高效穩定,特別是在資料結構複雜,資料量大的情況下,與其他通訊協議的差距尤為明顯。但不能跨語言。 2.HttpInvoker 使用 java 的序列化技術傳輸物件,與 RMI 在本質上是一致的。從效率上看,兩者也相差無幾, HttpInvoker 與 RMI 的傳輸時間基本持平。 3.Hessian 在傳輸少量物件時,比 RMI 還要快速高效,但傳輸資料結構複雜的物件或大量資料物件時,較 RMI 要慢 20% 左右。但這只是在資料量特別大, 資料結構很複雜的情況下才能體現出來,中等或少量資料時, Hessian並不比RMI慢。 Hessian 的好處是精簡高效,可以跨語言使用,而且協議規範公開, 我們可以針對任意語言開發對其協議的實現。另外, Hessian與WEB伺服器結合非常好,藉助WEB伺服器的成熟功能,在處理大量使用者併發訪問時會有很大優勢,在資源分配, 執行緒排隊,異常處理等方面都可以由成熟的WEB伺服器保證。而 RMI 本身並不提供多執行緒的伺服器。而且,RMI 需要開防火牆埠, Hessian 不用。 4.Burlap 採用 xml 格式傳輸。僅在傳輸 1 條資料時速度尚可,通常情況下,它的毫時是 RMI 的 3 倍。 5.Web Service 的效率低下是眾所周知的,平均來看, Web Service 的通訊毫時是 RMI 的 10 倍。 四、基本流程客戶端必須具備以下幾點: ·java客戶端包含Hessian.jar的包。 ·具有和伺服器端結構一樣的介面。 · 利用HessianProxyFactory呼叫遠端介面。 · 使用spring方式需要配置HessianProxyFactoryBean JAVA伺服器端必須具備以下幾點: ·包含Hessian的jar包。 ·設計一個介面,用來給客戶端呼叫。 ·實現該介面的功能。 ·配置web.xml,配好相應的servlet。 ·物件必須實現Serializable 介面。 ·對於spring方式DispatcherServlet攔截url,HessianServiceExporter提供Bean服務 五、原始碼分析 1. 客戶端發起請求 使用動態代理來實現的。除去 spring 對其的封裝,客戶端主要是通過 HessianProxyFactory 的 create 方法就是建立介面的代理類,該類實現了介面, JDK 的 proxy 類會自動用 InvocationHandler 的實現類(該類在 Hessian 中表現為 HessianProxy )的 invoke 方法體來填充所生成代理類的方法體 1、客戶端呼叫 hessian 服務時:
HessianProxy 類的:
invoke(Object proxy, Method method, Object []args){
String methodName = method.getName();// 取得方法名
Object value = args[0]; // 取得傳入引數
conn = sendRequest(mangleName, args) ; // 通過該方法和伺服器端取得連線
httpConn = (HttpURLConnection) conn;
code = httpConn.getResponseCode(); // 發出請求
// 等待伺服器端返回相應…………
InputStream is = conn.getInputStream();
AbstractHessianInput in = _factory.getHessianInput(is); //轉化為hessian自己的輸入輸出API
Object value = in.readObject(method.getReturnType()); // 取得返回值
}
URLConnection sendRequest(String methodName, Object []args){
URLConnection conn = _factory.openConnection(_url); // 建立 URLConnection
OutputStream os = conn.getOutputStream();
AbstractHessianOutput out = _factory.getHessianOutput(os); // 封裝為 hessian 自己的輸入輸出 API
out.call(methodName, args);
return conn;
}
2. 伺服器端接收請求並處理請求
伺服器端截獲相應請求交給:(Hessian2SkeletonInvoker) this.skeletonInvoker.invoke(inputStream, outputStream); org.springframework.remoting.caucho.HessianServiceExporter具體處理步驟如下:
a)HessianServiceExporter 類
(HessianExporter) invoke(request.getInputStream(), response.getOutputStream());
b)HessianExporter 類
(Hessian2SkeletonInvoker) this.skeletonInvoker.invoke(inputStream, outputStream);
c)Hessian2SkeletonInvoker 類
//將輸入輸出封轉化為轉化為 Hessian 特有的 Hessian2Input 和 Hessian2Output
Hessian2Input in = new Hessian2Input(isToUse);
in.setSerializerFactory(this.serializerFactory);
AbstractHessianOutput out = null;
int major = in.read();
out = new Hessian2Output(osToUse);
out.setSerializerFactory(this.serializerFactory);
(HessianSkeleton) this.skeleton.invoke(in, out);
d)HessianSkeleton 類
//讀取方法名
String methodName = in.readMethod();
Method method = getMethod(methodName);
//讀取方法引數
Class []args = method.getParameterTypes();
Object []values = new Object[args.length];
//執行相應方法並取得結果
result = method.invoke(service, values);
//結果寫入到輸出流
out.writeObject(result);
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hessian在Spring中的工作流程如下:
(1)客戶端:
a.傳送遠端請求:
客戶端程式-->呼叫公共介面的方法-->Hessian攔截器攔截請求-->封裝遠端呼叫請求-->Hessian代理-->通過HTTP協議傳送遠端請求代理到服務端
b.接收遠端呼叫響應:
遠端呼叫結果-->HTTP響應-->客戶端
(2)服務端:
a.接收遠端呼叫請求:
HessianServiceExporter接收請求-->將遠端呼叫物件封裝為HessianSkeleton框架-->HessianSkeleton處理遠端呼叫請求
b.返回遠端呼叫響應:
HessianSkeleton封裝遠端呼叫處理結果-->HTTP響應-->客戶端
下圖是通過hessian一次完成呼叫的示意圖
下面的程式碼是我自己在專案中用到的,大家可以參考,有更好的方法可以留言
SpringMVC 整合 Hessian
1、引用hessian-3.1.3.jar
2、新建一個hessian-servlet.xml
3、在web.xml 裡面新增
<!-- Spring 請求分發控制器 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:dispatcher-config.xml
</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
4、在dispatcher-config.xml 裡面新增配置資訊
這個檔案裡面根據你們自己的專案配置一些相關資訊
下面是我根據整合hessian加入的配置資訊
<mvc:annotation-driven />(一開始我自己沒有加入spring自動的攔截器配置在測試的時候老是出現錯誤問題)
<!-- Action 對映地址 -->
<import resource="dispatcher-action.xml"/>
<import resource="hessian-servlet.xml"/>
5、hessian-servlet.xml 中的配置
<!-- 業務類 -->
<bean id="pointPayServiceImpl" class="com.point.web.service.PointPayServiceImpl"/>
<!-- 遠端服務 -->
<bean name="hessianService" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="pointPayServiceImpl"/>
<property name="serviceInterface">
<value>
com.point.web.service.IPointPayService
</value>
</property>
</bean>
6、dispatcher-action.xml 配置
<!-- 配置url的mapping對映 -->
<bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hessian/alipay">hessianService</prop>
</props>
</property>
</bean>
上面內容相當於Server端
客戶端:
1、引用hessian-3.1.3.jar
2、在方法裡面寫
String payurl = "http://xxx:8080/demo/hessian/alipay";
HessianProxyFactory factory = new HessianProxyFactory();
//factory.setOverloadEnabled(true);
ITestService hessianService =( ITestService)factory.create( ITestService.class,payurl);
String message = hessianService.requestAlipay(param);
如果把客戶端變成服務端配置
web.xml:
<servlet>
<servlet-name>DemoHessian</servlet-name>
<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
<init-param>
<param-name>home-class</param-name>
<param-value>com.point.web.service.PointPayServiceImpl</param-value>
</init-param>
<init-param>
<param-name>home-api</param-name>
<param-value>com.point.web.service.IPointPayService</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DemoHessian</servlet-name>
<url-pattern>/hessianService</url-pattern>
</servlet-mapping>
需要注意的是:客戶端和服務端 介面、實現類和POJO的路徑必須一樣