1. 程式人生 > >Hessian 原理分析(轉)

Hessian 原理分析(轉)

Hessian 原理分析

 

 

一.      遠端通訊協議的基本原理

網路通訊需要做的就是將流從一臺計算機傳輸到另外一臺計算機,基於傳輸協議和網路 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 和 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 序列化的速度很快,而且序列化後的位元組數也較其他技術少。

 

 

 

 

 

參考文獻:

  1. 《 Java 遠端通訊可選技術及原理》 http://java.chinaitlab.com/base/740383.html
  2. 《 Hessian-3.2.0 原始碼》
  3. 《 hessian 序列化實現初探》 http://www.javaeye.com/topic/245238
  4. 《 Hessian 2.0 序列化協議規範》http://blog.csdn.net/xpspace/archive/2007/10/05/1811603.aspx

轉自:https://www.cnblogs.com/happyday56/p/4268249.html

Hessian 原理分析

 

 

一.      遠端通訊協議的基本原理

網路通訊需要做的就是將流從一臺計算機傳輸到另外一臺計算機,基於傳輸協議和網路 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 和 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 序列化的速度很快,而且序列化後的位元組數也較其他技術少。

 

 

 

 

 

參考文獻:

  1. 《 Java 遠端通訊可選技術及原理》 http://java.chinaitlab.com/base/740383.html
  2. 《 Hessian-3.2.0 原始碼》
  3. 《 hessian 序列化實現初探》 http://www.javaeye.com/topic/245238
  4. 《 Hessian 2.0 序列化協議規範》http://blog.csdn.net/xpspace/archive/2007/10/05/1811603.aspx