Hessian原理とプログラム設計

9533 ワード

Hessianは比較的よく使われるbinary-webserviceで、性能が高く、インターネット応用に適しており、主に一般的なwebserviceメソッド呼び出し、インタラクティブデータの小さいシーンで使用されている.hessianのデータインタラクションはhttpプロトコルに基づいており、通常hessianのserverエンド設計ではservletなどのweb serverコンテナに使用する必要がある.JavaクラスをHessianServiceletに暴露し、hessianサービスに公開することができます.ではhessian clientはservletを呼び出すのと同様に、リモートメソッドの出力結果を得ることができる.
hessianのインタフェース呼び出しはhttpに基づいてバイトコードでデータ交換されるため、hessianはカスタムの「protocol」およびシーケンス化/逆シーケンスメカニズムを提供する必要がある.一般にhessianのこの方式は効率的で簡潔であると考えられるが,hessianで用いられる反射機構/シーケンス化機構/動的エージェントはjava原生APIに基づいている.(Java独自のシーケンス化が効率的かどうかは、ここでは議論しません)
 
一.Hessianの原理とプロトコルの概要:
httpのプロトコルはデータ転送の方法を約束しており、hessianもあまり変更できません.
1)hessianにおけるclientとserverのインタラクションはhttp-post方式に基づいている.
2)hessianは補助情報をhttpヘッダにカプセル化し、例えば「許可token」など、httpヘッダに基づいて「セキュリティチェック」「metaデータ」などをカプセル化することができる.hessianは簡単な「検証」メカニズムを提供した.
3)hessianのインタラクティブコアデータ、例えば「呼び出されたメソッド」とパラメータリスト情報はpost要求のbody体を介して直接送信され、バイトストリームとしてフォーマットされる.
4)hessianのserver側応答データは,responseでバイトストリームにより直接出力される.
 
hessianの合意自体は複雑ではなく、ここでは余計なことは言わない.プロトコル(protocol)とは、制約データのフォーマットであり、clientはプロトコルに従って要求情報をバイトシーケンスにシーケンス化してserver側に送信し、server側はプロトコルに基づいてデータを逆シーケンス化して「オブジェクト」にし、指定した方法を実行し、メソッドの戻り値をプロトコルに従ってバイトストリームにシーケンス化し、clientに応答する.クライアントはプロトコルに従ってバイトストリームを逆シーケンスで「オブジェクト」にします.
 
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の全経路を知る必要がある.メソッドの呼び出しデータは、以下の「シーケンス」に従って送信される(通称「バイトコードフレーム」).
   
[“    “     ]["    ”    ][     ]{[    ][”  “     ][”  “     ]...}
例えばsayHello(String message)メソッドを呼び出すと、シーケンス化されたバイトストリームフォーマットは、次のようになり得る.
 
[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を1つずつ読み出して「8」を得、それから8バイトを読み出してutf 8でString文字列に符号化すると「sayHello」が得られ、このときserver側はclientが呼び出す必要がある方法名が「sayHello」であることを知ることができる.次に、32がintの1つについて、パラメータリストを表す個数が1である1を得る.その後、1バイトを読み出して「S」を取得し、パラメータがStringタイプであることを示す.....
 
サーバ側の実行が終了すると、Http応答のバイトストリームフォーマットは基本的に上記と同様です.では、HessianProxyは逆シーケンス化とタイプ変換を担当すればいいです.
 
Hessian Clientでは、デフォルトではリロードメソッドの呼び出しはサポートされていません.通常、OverloadEnabledプロパティをtrueに設定する必要があります.その後、メソッド呼び出し時に、Hessianは「メソッド署名」+「」+パラメータタイプは、新しいメソッド名としてサーバ側に送信されます. 
 
proxyFactory.setOverloadEnabled(true);//   false

 
 
サーバ側:
サーバ側で最も重要なクラスは、HessianServeretです.これは一般的なJavaサーブレット実装であり、各「サービス」はClientが送信したHttp要求を受信するHessianサーブレットインスタンスを構成する必要があり、要求タイプは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>
 
 
上記の例から、HessianServiceletには重要な初期化パラメータ「service-class」が必要であり、その値は良好な「リモートインタフェース」実装クラス(デフォルトコンストラクタ)である必要があることがわかります.HessianSkeletonとClient側のHessianProxyは、server側のHttp要求を担当するコアクラスであり、各HessianServiceはHessianSkeletonインスタンスを持ち、このインスタンスは「service-class」オブジェクトを持ち、初期化時に反射メカニズムによって「service-class」のすべてのメソッドリストを1つのmethodMapに配置し、keyは「メソッド名」であり、valueはMethod(java.lang.reflect.Method)であり、メソッドのリロードを回避するために「メソッド名_パラメータタイプ」を新しいキーとして追加し、methodMapにも入れます.
 
HessianServiceletが初期化されると、反射メカニズムによって「Services-class」のインスタンスが作成され、HttpリクエストがServiceletに到達すると、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のインタラクティブデータのバイナリストリームを追加的に暗号化しようとしないでください.これは、安全に見えますが、実際には効果がありません.
 
二.httpリクエストと「べき乗等性」:
「べき乗等性」とは、1つのリソースに対して同じパラメータを使用してアクセスすることであり、毎回得られる結果は同じであるべきであるか、あるいは「リソースコンテンツの変更」は予想に合致する(例えば、デジタル自己増加操作);ここでhttp getリクエストは「べき乗等」とみなされるが、httpではpostリクエストによりserver側データが変更され、場合によってはserver側の商品数を「減算」するなどの「べき乗等性」を破る可能性があり、リクエスト送信に成功するとserverが実行に成功するが、ネットワーク異常によりclientが結果を受けられず、このときclientが再試行すると、サーバ側が再実行することになりますが、実際には、数は1回繰り返し計算されます.
http-webserviceにとって、データ変更とのインタフェース呼び出し、またはインタフェースで合理的な論理検証を行うことに注意する必要があります.または、「2段階」コミットと同様の方法で制御します.
1)まず要求を送信し、versionを取得します.このversionはデータベースの楽観的なロックのversionであるか、redis/zookeeperなどのデータセンターの唯一の値である可能性があります.たとえば、受注テーブルでは、orderIdごとにversionが対応しており、データ操作のたびにversion++になります.では、hessianインタフェースで受注情報を変更する必要がある場合は、まずこのversionを取得します.
2)clientは,変更が必要な情報を伝達するとともに,1)で取得したversionを渡す.ではserver側がデータ変更を実行する場合、まずversionが一致しているかどうかを検出し、一致している場合は修正し、そうでない場合はclientに「再試行」信号を応答すると、clientはversionを再取得し、コミットを続行する必要があります.
 
 
三.コアAPI:
 
    1) HessianProxy
    2) HessianProxyFactory
3)HessianInput:応答を逆シーケンス化するためのストリーム制御を入力し、remote側の異常スタック(client側で再放出される)、「Fault」情報(フォーマットエラーなどのremote側の失敗情報)を含む.多くの場合、http response-code値を指定することで、特定のリクエスト失敗信号を実現することができます.
4)HessianOutput:要求されたデータをシーケンス化するためのストリーム制御を出力する
5)Deserializer:逆シーケンス化インタフェース、大量の実装クラスは逆シーケンス化の異なるタイプのデータに用いる
6)Serializer:シリアル化インタフェース
 
四.プログラムインスタンス(spring MVCベース):
 
    1. web.xml(サーバ側)
<servlet>
	<servlet-name>hessianService</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mvc-servlet.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>hessianService</servlet-name>
	<url-pattern>/hessian/*</url-pattern>
</servlet-mapping>

ここで奇妙な問題があります.hessianがspringでパブリッシュされている場合、url-patternは「/hessian」接頭辞で始まる必要があります. 
 
     2. spring-mvc-servlet.xml(サーバ側)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" default-autowire="byName">
           
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> 
    <import resource="hessian-servlet.xml"/>

</beans>

Autowireは「byName」であり、mapping方式を「BeanName UrlHandlerMaping」と指定する必要があります.springがリリースしたhessianサービスがあり、beanNameはservlet urlの一部として使用できます.
 
    3. hessian-servlet.xml(サーバ側)
<bean id="helloService" class="com.test.remote.impl.HelloServiceImpl" />
<bean name="/userService" class="org.springframework.remoting.caucho.HessianServiceExporter">
  <property name="service" ref="userService"/>
  <property name="serviceInterface" value="com.test.remote.HelloService"/>
</bean>

 
    4. クライアント・サービス構成
<!-- autowire: byName -->
<bean id="helloService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">  
	<property name="serviceUrl" value="http://localhost:8080/hessian/helloService" />  
	<property name="serviceInterface" value="com.test.remote.HellowService" />
	<property name="overloadEnabled" value="true"/>
</bean> 

その後、開発者は他のspring beanのようにhelloServiceを使用することができます.