Hessian是比較常用的binary-rpc。性能較高,適合互聯網應用。主要使用在普通的webservice 方法調用。交互數據較小的場景中。hessian的數據交互基於http協議,一般hessian的server端設計需要使用到web server容器(比方servlet等)。你可以將不論什麼Java類暴露給HessianServlet,並公佈成hessian服務;那麼hessian client將可以經過相似調用servlet同樣,得到遠程方法的輸出結果。java
因爲hessian的接口調用基於http,且以字節碼的方式進行數據交換,那麼hessian需要提供本身定義的「protocol」以及序列化/反序列機制。web
一般咱們以爲hessian的這樣的方式是高效而且簡潔的,只是hessian中使用的反射機制/序列化機制/動態代理都是基於java原生API。(Java自帶的序列化是否高效,在此就不在爭論了)redis
一.Hessian原理與協議簡析:spring
http的協議約定了傳輸數據的方式,hessian也沒法改變太多:數據庫
1) hessian中client與server的交互,基於http-post方式。spring-mvc
2) hessian將輔助信息。封裝在http header中,比方「受權token」等,咱們可以基於http-header來封裝關於「安全校驗」「meta數據」等。hessian提供了簡單的"校驗"機制。安全
3) 對於hessian的交互核心數據,比方「調用的方法」和參數列表信息。將經過post請求的body體直接發送。格式爲字節流。cookie
4) 對於hessian的server端響應數據,將在response中經過字節流的方式直接輸出。網絡
hessian的協議自己並不複雜。在此再也不贅言。所謂協議(protocol)就是約束數據的格式,client依照協議將請求信息序列化成字節序列發送給server端,server端依據協議,將數據反序列化成「對象」,而後運行指定的方法。並將方法的返回值再次依照協議序列化成字節流,響應給client,client依照協議將字節流反序列話成"對象"。mvc
Client端:
在Client端,核心API爲:
1) HessianProxyFactory: 負責託管"遠程接口"和"遠程hessian服務的URL",並生成代理類(Java Proxy實例)。
2) HessianProxy: Proxy實例的驅動器(handler),當代理實例的方法調用時,HessianProxy負責序列化"方法名"/"參數列表"等,並調用遠程URL獲取響應數據;同一時候也負責反序列化。
底層使用HttpURLConnection。
3) HessianOutput: 負責將序列化的字節數據,依照協議,寫入inputStream,並經過URL Connection發送給遠端。
hessian-client發送請求的首要條件,就是需要指明url,此url就是server端暴露的servlet地址。
- HessianProxyFactory proxyFactory = new HessianProxyFactory();
- HelloService service = (HelloService)proxyFactory.create(HelloService.class, "http://localhost:8080/hessian/helloService");
- System.out.println(service.sayHello("hessian"));
上述代碼例子中,HelloService表示遠程接口API,當中URL中「helloService」表示調用的「服務名稱」,這個「服務名稱」有server端決定。因此Client需要首先知道所需服務的URL全路徑。
那麼方法的調用數據,將會依照例如如下「序列」發送(俗稱「字節碼成幀」):
- [「方法名稱「的字節長度]["方法名稱」字節序列][參數的個數]{[參數類型][」參數「的字節長度][」參數「的字節序列]...}
- [8][sayHello][1]['S'][5]['hello']
- //當中「8」表示「sayHello」方法名稱爲8個字節
- //"1"表示參數的個數爲1
- //「S」表示參數的類型爲「String」,hessian中定義了大量的簡寫字母。用來表示java數據類型
- //「5」表示參數的字節個數爲5
- //"hello"表示此參數的值爲「hello」,只是實際上傳輸的應該是「hello」相應的字節序列。
假設你從事過socket通訊方面的開發。你應該知道server端會怎樣「解析」這個字節流信息。對於Hessian-server端而言,也是依據「字節碼成幀」,逐個讀取信息,比方首先讀取一個32位的int。獲得「8」,而後讀取8個字節並使用utf8的方式編碼成String字符串,將得到「sayHello」,此時server端已經可以知道client需要調用的方法名稱爲「sayHello」。而後對於一個32爲的int。獲得1,表示參數列表的個數爲1;而後在讀取一個字節。得到「S」,表示參數爲String類型.....
Server端運行結束後,Http響應的字節流格式基本和上述相似。
那麼HessianProxy負責反序列化和類型轉換就能夠。
需要注意。Hessian Client默認不支持「重載」方法的調用,一般咱們需要開啓「OverloadEnabled」屬性設置爲true。此後在方法調用時。Hessian將會把「方法簽名」 + 「_」 + 「參數類型」做爲新的方法名稱發送給Server端。
- proxyFactory.setOverloadEnabled(true);//默以爲false
Server端:
在Server端最重要的類,就是HessianServlet;它是一個普通的Java Servlet實現。每個「服務」都需要配置一個HessianServlet實例,它負責接收Client發送的Http請求,請求類型必須是POST。
- <servlet>
- <servlet-name>helloService</servlet-name>
- <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
- <init-param>
- <param-name>service-class</param-name>
- <param-value>com.test.hessian.impl.HelloServiceImpl</param-value>
- </init-param>
- </servlet>
- <servlet-mapping>
- <servlet-name>helloService</servlet-name>
- <url-pattern>/hessian/helloService</url-pattern>
- </servlet-mapping>
從上述實例中。咱們看出HessianServlet需要一個重要的初始化參數「service-class」,它的值必須是一個良好的「遠程接口」實現類(默認構造器)。
HessianSkeleton和Client端的HessianProxy相應,它是負責server端Http請求的核心類,每個HessianServlet都會持有一個HessianSkeleton實例,這個實例持有「service-class」對象。並在初始化時經過反射機制將「service-class」的所有方法列表放在一個methodMap中,key爲「方法名」,value爲Method(java.lang.reflect.Method),同一時候爲了不方法重載。還會額外的將「方法名_參數類型」做爲一個新key。也放入methodMap中。
在HessianServlet初始化時,會經過反射機制的方式建立一個「service-class」的實例,當Http請求到達Servlet時,HessianSkeleton實例負責從請求中解析出「方法名」和參數列表;那麼到此爲止。一切就很是清楚了,HessianSkeleton依據方法名,從methodMap中獲取Method,並調用其invode方法。而後將運行結果反序列化。寫入Response。
1) Hessian的client每次調用,都會開啓一個新的http連接,因此各個調用之間沒法共享數據,其實你可以使用cookie技術來保存相關數據。但是其實收效甚微。
2) Hessian的client使用了java的動態代理,因爲client需要明白知道接口的API信息。
3) Hessian的接口API中,不論是方法的參數仍是方法的返回值,都必須是可序列化的(實現Serializable接口),你可以經過重寫Serializable接口的相關方法。來本身定義序列化/反序列化操做。
4) 不要嘗試使用hessian交互大數據。
5) 不要嘗試對hessian的交互數據的二進制流進行額外的加密,這是一件得不償失的事情,儘管看起來安全,其實收效甚微。