Hessian 原理分析 html
一. 遠程通信協議的基本原理 java
網絡通訊須要作的就是將流從一臺計算機傳輸到另一臺計算機,基於傳輸協議和網絡 IO 來實現,其中傳輸協議比較出名的有 http 、 tcp 、 udp 等等, http 、 tcp 、 udp 都是在基於 Socket 概念上爲某類應用場景而擴展出的傳輸協議,網絡 IO ,主要有 bio 、 nio 、 aio 三種方式,全部的分佈式應用通信都基於這個原理而實現,只是爲了應用的易用,各類語言一般都會提供一些更爲貼近應用易用的應用層協議。spring
二. 應用級協議 Binary-RPC api
Binary-RPC 是一種和 RMI 相似的遠程調用的協議,它和 RMI 的不一樣之處在於它以標準的二進制格式來定義請求的信息 ( 請求的對象、方法、參數等 ) ,這樣的好處是什麼呢,就是在跨語言通信的時候也可使用。服務器
來看下 Binary -RPC 協議的一次遠程通訊過程:網絡
1 、客戶端發起請求,按照 Binary -RPC 協議將請求信息進行填充;tcp
2 、填充完畢後將二進制格式文件轉化爲流,經過傳輸協議進行傳輸;分佈式
3 、接收到在接收到流後轉換爲二進制格式文件,按照 Binary -RPC 協議獲取請求的信息並進行處理;工具
4 、處理完畢後將結果按照 Binary -RPC 協議寫入二進制格式文件中並返回。源碼分析
問題總結:
1 、傳輸的標準格式是?
標準格式的二進制文件。
2 、怎麼樣將請求轉化爲傳輸的流?
將二進制格式文件轉化爲流。
3 、怎麼接收和處理流?
經過監聽的端口獲取到請求的流,轉化爲二進制文件,根據協議獲取請求的信息,進行處理並將結果寫入 XML 中返回。
4 、傳輸協議是?
Http 。
三. Hessian ——一種實現遠程通信的 library
Hessian 是由 caucho 提供的一個基於 binary-RPC 實現的遠程通信 library 。
1 、是基於什麼協議實現的?
基於 Binary-RPC 協議實現。
2 、怎麼發起請求?
需經過 Hessian 自己提供的 API 來發起請求。
3 、怎麼將請求轉化爲符合協議的格式的?
Hessian 經過其自定義的串行化機制將請求信息進行序列化,產生二進制流。
4 、使用什麼傳輸協議傳輸?
Hessian 基於 Http 協議進行傳輸。
5 、響應端基於什麼機制來接收請求?
響應端根據 Hessian 提供的 API 來接收請求。
6 、怎麼將流還原爲傳輸格式的?
Hessian 根據其私有的串行化機制來將請求信息進行反序列化,傳遞給使用者時已經是相應的請求信息對象了。
7 、處理完畢後怎麼迴應?
處理完畢後直接返回, hessian 將結果對象進行序列化,傳輸至調用端。
四. Hessian 源碼分析
以 hessian 和 spring dm server 整合環境爲例。
Hessian 的這個遠程過程調用,徹底使用動態代理來實現的。有客戶端能夠看出。
除去 spring 對其的封裝,客戶端主要是經過 HessianProxyFactory 的 create 方法就是建立接口的代理類,該類實現了接口, JDK 的 proxy 類會自動用 InvocationHandler 的實現類(該類在 Hessian 中表現爲 HessianProxy )的 invoke 方法體來填充所生成代理類的方法體。
客戶端系統啓動時:
根據 serviceUrl 和 serviceInterface 建立代理。
HessianProxyFactoryBean 類
HessianClientInterceptor 類
createHessianProxy(HessianProxyFactory proxyFactory)
HessianProxyFactory 類
public Object create(Class api, String urlName)
客戶端調用 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(); // 發出請求
// 等待服務器端返回相應…………
is = conn.getInputStream();
Object value = in.readObject(method.getReturnType()); // 取得返回值
HessianProxy 類的 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;
服務器端截獲相應請求交給:
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();
int minor = in.read();
out = new Hessian2Output(osToUse);
out = new HessianOutput(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 本身的 API 。輸入輸出流都要封裝爲 hessian 本身的 Hessian2Input 和 Hessian2Output ,接下來一節咱們將去了解 hessian 本身封裝的輸入輸出到底作了些什麼!
五. Hessian 的序列化和反序列化實現
hessian 源碼中 com.caucho.hessian.io 這個包是 hessian 實現序列化與反序列化的核心包。其中 AbstractSerializerFactory , AbstractHessianOutput , AbstractSerializer , AbstractHessianInput , AbstractDeserializer 是 hessian 實現序列化和反序列化的核心結構代碼。
根據類來決定用哪一種序列化工具類
abstract public Serializer getSerializer(Class cl) throws HessianProtocolException;
根據類來決定用哪一種反序列化工具類
abstract public Deserializer getDeserializer(Class cl) throws HessianProtocolException;
在 SerializerFactory 有不少靜態 map 用來存放類與序列化和反序列化工具類的映射,這樣若是已經用過的序列化工具就能夠直接拿出來用,沒必要再從新實例化工具類。
在 SerializerFactory 中,實現了抽象類的 getSerializer 方法,根據不一樣的須要被序列化的類來得到不一樣的序列化工具,一共有 17 種序列化工具, hessian 爲不一樣的類型的 java 對象實現了不一樣的序列化工具,默認的序列化工具是 JavaSerializer 。
在 SerializerFactory 中,也實現了抽象類的 getDeserializer 方法,根據不一樣的須要被反序列化的類來得到不一樣的反序列化工具,默認的反序列化工具類是 JavaDeserializer 。
它會實現不少方法,用來作流輸出。
須要注意的是方法,它會先調用 serializerFactory 根據類來得到 serializer 序列化工具類
public void writeObject(Object object)
throws IOException
{
if (object == null) {
writeNull();
return;
}
Serializer serializer;
serializer = _serializerFactory.getSerializer(object.getClass());
serializer.writeObject(object, this);
}
其 writeObject 是必須在子類實現的方法, AbstractSerializer 有 17 種子類實現, hessian 根據不一樣的 java 對象類型來實現了不一樣的序列化工具類,其中默認的是 JavaSerializer 。
而 JavaSerializer 的 writeObject 方法的實現,遍歷 java 對象的數據成員,根據數據成員的類型來得到各自的 FieldSerializer ,一共有 6 中默認的 FieldSerializer 。
拿默認的 FieldSerializer 舉例,仍是調用 AbstractHessianOutput 的子類來 writeObject ,這個時候,確定能找到相應的 Serializer 來作序列化
同理能夠反推出 hessian 的反序列化機制。 SerializerFactory 能夠根據須要被反序列化的類來得到反序列化工具類來作反序列化操做。
總結:得益於 hessian 序列號和反序列化的實現機制, hessian 序列化的速度很快,並且序列化後的字節數也較其餘技術少。
參考文獻:
http://blog.csdn.net/xpspace/archive/2007/10/05/1811603.aspx