一. ????? ? 遠程通訊協議的基本原理
網絡通信需要做的就是將流從一臺計算機傳輸到另外一臺計算機,基于傳輸協議和網絡 ? IO ? 來實現,其中傳輸協議比較出名的有 ? http ? 、 ? tcp ? 、 ? udp ? 等等, ? http ? 、 ? tcp ? 、 ? udp ? 都是在基于 ? Socket ? 概念上為某類應用場景而擴展出的傳輸協議,網絡 ? IO ? ,主要有 ? bio ? 、 ? nio ? 、 ? aio ? 三種方式,所有的分布式應用通訊都基于這個原理而實現,只是為了應用的易用,各種語言通常都會提供一些更為貼近應用易用的應用層協議。
二. ????? ? 應用級協議 ? Binary-RPC
Binary-RPC ? 是一種和 ? RMI ? 類似的遠程調用的協議,它和 ? RMI ? 的不同之處在于它以標準的二進制格式來定義請求的信息 ? ( ? 請求的對象、方法、參數等 ? ) ? ,這樣的好處是什么呢,就是在跨語言通訊的時候也可以使用。
來看下 ? Binary -RPC ? 協議的一次遠程通信過程:
?
1 ? 、客戶端發起請求,按照 ? Binary -RPC ? 協議將請求信息進行填充;
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 就是把 Java 對象轉變成 ? 字節序列,然后通過 Http 傳輸到 ? 目標服務器上(主機 2 ),主機 2 收到這個字節序列后,按照一定的協議標準進行反序列,提交給對應的服務處理。處理完成以后以同樣的方式返回數據。
?
現在我們回頭看看例子中的配置( WEB-INF.XML ) :
配置的 Servlet : ? com.caucho.hessian.server.HessianServlet
對應的參數:接口 (home-api) : com.alisoft.enet.hessian.Hello
???????????????????????????? 實現 (home-class): com.alisoft.enet.hessian.HelloImpl
?
?
?
HessianServlet? 中的實現代碼如下(略過部分代碼):
?
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();
//輸入流
Hessian2Input in = new Hessian2Input(is);
SerializerFactory serializerFactory = getSerializerFactory();
in.setSerializerFactory(serializerFactory);
//輸出流
AbstractHessianOutput out;
int major = in.read();
int minor = in.read();
out = new Hessian2Output(os);
out.setSerializerFactory(serializerFactory);
_homeSkeleton.invoke(in, out);
?
整個執行步驟如下:
l ?? 接收輸入流,并通過 SerializerFactory 轉化為 ?Hessian? 特有的 ? Hessian2Input
l ?? 設置輸出流,并通過 SerializerFactory 轉化為 ?Hessian? 特有的 ? Hessian2Output
l ?? 根據配置的接口和實現參數,調用服務,并把結果寫入到輸出流 ? Hessian2Output 中
l ??? Out.close()
?
Hessian 遠程訪問基于序列化和反序列化的方式。當程序運行時,程序所創建的各種對象都位于內存中,當程序運行結束,這些對象就結束了生命周期。對象的序列化主要有兩種用途:
l ?? 把對象的字節序列永久地保存到硬盤上,通常是放在一個文件中。
l ?? 在網絡上傳輸對象的字節序列
?
?
四. ????? ? Hessian ? 源碼分析
以 ? hessian ? 和 ? spring dm server ? 整合環境為例。
1. ???? ? 客戶端發起請求
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;
????????
?
2. ???? ? 服務器端接收請求并處理請求
服務器端截獲相應請求交給:
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 ? 實現序列化和反序列化的核心結構代碼。
?
1. ???????? ? AbstractSerializerFactory ? ,它有 ? 2 ? 個抽象方法:
根據類來決定用哪種序列化工具類
abstract public Serializer getSerializer(Class cl) ? ? throws HessianProtocolException; ?
根據類來決定用哪種反序列化工具類
abstract public Deserializer getDeserializer(Class cl) ? ? throws HessianProtocolException;
2. ???????? ? SerializerFactory ? 繼承 ? AbstractSerializerFactory ? 。
在 ? SerializerFactory ? 有很多靜態 ? map ? 用來存放類與序列化和反序列化工具類的映射,這樣如果已經用過的序列化工具就可以直接拿出來用,不必再重新實例化工具類。
在 ? SerializerFactory ? 中,實現了抽象類的 ? getSerializer ? 方法,根據不同的需要被序列化的類來獲得不同的序列化工具,一共有 ? 17 ? 種序列化工具, ? hessian ? 為不同的類型的 ? java ? 對象實現了不同的序列化工具,默認的序列化工具是 ? JavaSerializer ? 。
在 ? SerializerFactory ? 中,也實現了抽象類的 ? getDeserializer ? 方法,根據不同的需要被反序列化的類來獲得不同的反序列化工具,默認的反序列化工具類是 ? JavaDeserializer ? 。
3. ???????? ? HessianOutput ? 繼承 ? AbstractHessianOutput ? 成為序列化輸出流的一種實現。
它會實現很多方法,用來做流輸出。
需要注意的是方法,它會先調用 ? 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); ?
} ?
4. ???????? ? 現在我們來看看 ? AbstractSerializer ? 。
其 ? writeObject ? 是必須在子類實現的方法, ? AbstractSerializer ? 有 ? 17 ? 種子類實現, ? hessian ? 根據不同的 java ? 對象類型來實現了不同的序列化工具類,其中默認的是 ? JavaSerializer ? 。
而 ? JavaSerializer ? 的 ? writeObject ? 方法的實現,遍歷 ? java ? 對象的數據成員,根據數據成員的類型來獲得各自的 ? FieldSerializer ? ,一共有 ? 6 ? 中默認的 ? FieldSerializer ? 。
拿默認的 ? FieldSerializer ? 舉例,還是調用 ? AbstractHessianOutput ? 的子類來 ? writeObject ? ,這個時候,肯定能找到相應的 ? Serializer ? 來做序列化
?
同理可以反推出 ? hessian ? 的反序列化機制。 ? SerializerFactory ? 可以根據需要被反序列化的類來獲得反序列化工具類來做反序列化操作。
?
總結:得益于 ? hessian ? 序列號和反序列化的實現機制, ? hessian ? 序列化的速度很快,而且序列化后的字節數也較其他技術少。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
