アプリケーション開発にとって非同期 Web サービスの実装と呼び出しは重要です。Web サービスの機能を公開する UI は、より一層対話型になってきました。そのため非同期の呼び出しと実装は、より便利で効果的なものになっており、結果的にユーザー・エクスペリエンス全体を改善する上で役立っています。この記事では非同期の Web サービスにおけるシナリオのさまざまなパターンの概要を説明し、またそれらを Apache Axis2 を使って実装する方法についても説明します

Eran Chinthaka (chinthaka@apache.org), Member, Apache Software Foundation

Eran ChinthakaEran Chinthaka は、Apache Axis2 や Axiom、Synapse などのプロジェクトの当初からのメンバーであり、WSDL 2.0 と WS-Addressing のワーキング・グループのメンバーでもあります。



2007年 10月 11日

はじめに

アプリケーション開発を行うための方法として Web サービスの実装が最初に導入された当時には、リクエストとレスポンスの対話動作用にサポートされていたのは同期呼び出しのみでした (この記事でいう同期は、リクエストとレスポンスの両方を同じ実行スレッドで処理することを意味します)。しかし、Web サービスを使って機能を公開するアプリケーションや、Web サービスと対話動作するように設計されたクライアント・アプリケーションが増えるにつれ、単なる同期呼び出しはボトルネックと見られるようになりました。これは、一部の Web サービスの実装が (サービス実装内部のさまざまな理由から) リクエストに応答するために非常に長い時間を要するためです。例えば、Web サービスが人間の介在を必要とする場合や、ある時点でバッチ処理を必要とする場合には、その Web サービスが結果を出すまでに何日もかかるかもしれません。こうした遅延によって、トランスポート機構の一部がタイムアウトしてしまうこともありました。

Web サービスの開発が進化するにつれ、やがて一部の SOAP スタックは同期機能の上で非同期呼び出しをエミュレートするようになりました。こうした努力によって非同期呼び出しに似たようなことはできましたが、完全な真の非同期機能を提供することはできませんでした。Apache Software Foundation によるオープンソースの Web サービス・エンジンである Apache Axis2 は、非同期 Web サービスの呼び出しと実装に対して第一級のサポートを提供しています。Axis2 は、クライアント・サイドで非同期動作を実現する機構を 2 つ、サーバー・サイドで非同期動作を実現する機構を 1 つ持っています。それでは、これらの機構を使って非同期通信を行うための方法を詳しく見てみましょう。

クライアント・サイドの非同期動作

クライアント・サイドの非同期動作によって、リクエスト・メッセージとレスポンス・メッセージを別々の 2 つのスレッドで処理することができます。これをクライアント・サイドで実装するためには 2 つの方法があります。これを次のセクションで説明します。

非ブロッキング API

クライアントに非ブロッキング API を持たせると、クライアントはリクエストを SOAP エンジンに渡して他の作業を継続することができます。SOAP エンジンはサーバーからレスポンスを受信すると、クライアントから提供されたコールバック・オブジェクトを使って通知を行います。この方法は、GUI を持つ対話型アプリケーションの場合に特に便利です。

Axis2 クライアント API は、一般的に使われる MEP (Message Exchange Pattern) のためのメソッドを提供します。IN-OUT 対話動作を使って非同期のサポートを説明しましょう。

ServiceClient API を使う場合には、sendReceive(OMElement) メソッドまたはこのメソッドを同期呼び出し用に対応させたメソッドを使います。一方で sendReceiveNonBlocking(OMElement, Callback) は、Axis2 クライアント API の持つ、基本となる非同期動作を提供します (リスト 1)。

リスト 1. クライアントをブロックせずに IN-OUT Web サービスを呼び出す
// First create the payload of the message.
OMElement payload = getPayload(); // add your payload here

// Create an instance of service client.
ServiceClient serviceClient = new ServiceClient();

// Create an options object to pass parameters to service client.
Options options = new Options();
            
// Set the location of the Web service.
options.setTo(new EndpointReference
  ("http://www.sample-web-service.com/StockQuote/getQuote"));
serviceClient.setOptions(options);
            
// Create a callback object.
Callback callback = new MyCallback();

// Do an async invocation.
serviceClient.sendReceiveNonBlocking(payload, callback);

// Continue doing other work without waiting until the response comes.

クライアントが非ブロッキング API を使って Web サービスを呼び出す場合には、クライアントは Axis2 の ServiceClient API にコールバック・オブジェクトを渡す必要があります。Axis2 エンジンは、レスポンス・メッセージを受信すると、このレスポンス・メッセージをコールバック・オブジェクトに渡します。するとコールバック・オブジェクトは、このレスポンスをクライアントに代わって処理するか、あるいはクライアントに渡します。

今度はこの Callback の実装方法を見てみましょう。このコールバック・インターフェースには次の 2 つのメソッドがあります。

  • onComplete(AsyncResult) メソッドは、レスポンスの受信が終わると呼び出されます。
  • AsyncResult オブジェクト・メソッドは、レスポンスを受信した結果作成される SOAP エンベロープとメッセージのコンテキストをカプセル化します。

クライアントは、Callback インターフェースを継承することで、このメソッドの中でレスポンスを処理することができます。一方、もし呼び出し中にエラーが発生すると onError(Exception) メソッドが呼び出されます。この詳細をリスト 2 で見てみましょう。

リスト 2. Callback インターフェースを実装する
 class MyCallback extends Callback {

  public void onComplete(AsyncResult result) {
     // Get the response SOAP envelope from the result.
     SOAPEnvelope envelope = result.getResponseEnvelope();
            
     // Process SOAP envelope. 
   }

   public void onError(Exception e) {
     // Write here what you want to do in case of an error.
   }
}

Callback クラスをポーリング用の機構として使うこともできます。クライアントは、Callback.isComplete() メソッドを呼び出すことで連続的にレスポンスをポーリングすることができます。これは、複数の Callback インスタンスがレスポンスを待っている場合や、レスポンスをモニターして実行中のアプリケーションを更新する中心的なコントローラーがある場合に便利です。Callback クラスはこのように、非同期呼び出しを行う際のコールバック機構としても、あるいはポーリング機構としても使うことができます。

トランスポート・レベルの非同期動作

2 番目の方法は、トランスポート・レベルの非同期動作を使います。Web サービスの呼び出しに使用されるすべてのトランスポートは、大きく分けて 2 つのタイプの呼び出しに分類することができます。

  • 片方向トランスポートはメッセージの送信または受信のいずれかに対してチャネルを提供します。
  • 双方向トランスポートは同じチャネルを使ってメッセージを送受信することができます。

SMTP (Simple Mail Transfer Protocol) は片方向トランスポートの例であり、HTTP は双方向トランスポートの例です。Web サービスとの間でリクエストとレスポンスによる対話動作を行う場合には、HTTP 用に 1 チャネルを使うだけですが、SMTP を使う場合には 2 つのチャネルが必要です。

リクエストとレスポンスの対話動作に関して、クライアントはさまざまな方法を使うことができます。最も簡単な方法は HTTP のような双方向トランスポートを使う方法です。しかしこの方法ではトランスポート・レベルの非同期動作が使えません。クライアントは、片方向トランスポートを 2 つ、あるいは双方向トランスポートを 2 つ使うこともできます。例えば、SMTP 用に 1 チャネルを使ってメッセージを送信し、もう 1 つ SMTP 用のチャネルを使ってレスポンス・メッセージを受信することができます。同時に、HTTP 用のチャネルを使ってリクエストを送信し、メッセージの送信後にこのチャネルを閉じ、そして別の HTTP 用のチャネルを使ってレスポンスを受信することができます。しかしこの方法を使うためには、メッセージを関連付けるための仕組みが必要です。Apache Axis2 は WS-Addressing 仕様を使ってメッセージの関連付けを行っています。以下で説明するのは、さまざまなトランスポートを処理するための方法です。

まず、任意のトランスポート・チャネルを使ってリクエストを送信し、別のチャネルで同じタイプのトランスポートを使ってレスポンスを受信します。Axis2 クライアントは、自分が別のリスナーを使ってレスポンス・メッセージを受信しようとしていることを、useSeparateListener フラグを使って通知することができます。別の言い方をすれば、このフラグを有効にすると、この呼び出しに対して 2 つの異なるトランスポート・チャネルを使うように Axis2 エンジンに指示していることになります。Axis2 の中で最も使いやすいクライアント API であるServiceClient API には、この値を設定するためのメソッド setUseSeparateListener(boolean) があります。リスト 3 は useSeparateListener API を使って Web サービスを呼び出す方法を示しています。

リスト 3. useSeparateListener 変数を使う
try {
  // first create the payload of the message
  OMElement payload = getPayload(); // add your payload here

  // create an instance of service client
  ServiceClient serviceClient = new ServiceClient();

  // create an options object to pass parameters to service client
  Options options = new Options();
            
  // set the location of the web service
  options.setTo(new EndpointReference
    ("http://www.sample-web-service.com/StockQuote/getQuote"));
            
  // inform Axis2 engine that you need to use a different channel for the response
  options.setUseSeparateListener(true);
            
  serviceClient.setOptions(options);
            
  // invoke the service
  serviceClient.sendReceive(getPayload());

 } catch (AxisFault axisFault) {
  axisFault.printStackTrace();
 }

ここでは Axis2 エンジンに対して、リクエストの送信に使用したものと同じタイプのトランスポートからトランスポート・リスナーを準備するように指示しています。また Axis2 エンジンに対して、レスポンスを受信するようにも指示しています。使用される適切なトランスポートは、エンドポイントの参照値から推論することができます。この例では、メッセージを http://www.sample-web-service.com/StockQuote/getQuote に送信しようとしており、またこの呼び出しには 2 つの HTTP トランスポートが使われています。Web サービスを呼び出すためには、sendReceive メソッドまたは sendReceiveNonBlocking メソッドのどちらかを使うことができます。

今度は Axis2 を使ってサーバー・サイドの非同期動作を実現する方法を調べましょう。


サーバー・サイドの非同期動作

SOAP エンジンが SOAP リクエストを受信する場合、通常の方法ではリクエストの処理とメッセージの送信を同じスレッドで行います。しかし、この方法が必ずしも適切であるとは限りません。例えば、リクエストの処理に非常に時間がかかる場合には、トランスポート・チャネルがタイムアウトしてしまうかもしれません。あるいは場合によると、その期間中リソースを保持することは、大量のトラフィックを処理するシステムにとって適切なことではないかもしれません。つまり理想的な方法は、サーバーがメッセージを受信したことをクライアントに通知し、後から別のトランスポート・チャネルを使って応答を送信することです。

Axis2 はメッセージ受信プログラムを採用しており、このプログラムが MEP を処理し、メッセージをリクエスト処理ロジックまたはサービス実装に渡します。同期呼び出しの場合には、このメッセージ受信プログラムは (サービス実装が Java™ 言語で作成されていれば) サービス実装クラスの適切なメソッドを呼び出し、そして同じスレッドにレスポンスを送信します。しかし非同期呼び出しの場合には、このメッセージ受信プログラムは新しいスレッドを起動してサービスを呼び出し、クライアントに通知を送信します。この新しいスレッドは、クライアントに対してレスポンスを送信し、そしてサービス実装クラスからレスポンスを取得します。これが Axis2 でのサーバー・サイドの非同期動作です。今度は、これを Web サービスに使うためにはどうすればよいかを調べてみましょう。

サービスを作成するための基本的な方法には、次の 2 つがあります。

  • WSDL (Web Services Description Language) を作成し、次にサービス実装クラス用のコードを生成して WSDL に入力する。
  • サービス実装クラスと services.xml を作成し、それを Axis2 の中にデプロイする。

services.xml にはデプロイされる Web サービスに関するメタデータが含まれ、サービス実装クラスと、公開されるメソッド、そして Web サービスに渡す必要のあるすべてのパラメーターがリストされています。

上記の、WSDL を作成してからコードを生成する方法を開始する場合には、サービスの非同期呼び出しを有効にするようにコード生成プログラムに対して指示します。するとコード生成プログラムは、サービスを呼び出すための非同期メッセージ受信プログラムを生成します。指定された WSDL ファイルから Java コードを生成するために WSDL2Java スクリプトを実行する場合には、-a オプションを指定してこのスクリプトが非同期コードを生成するように指示します。2 番目の、最初にコードを作成する方法を使う場合には、services.xml の中で IN-OUT 呼び出しに対して org.apache.axis2.async.AsyncMessageReceiver を関連付けます。それには、IN-OUT の MEP を org.apache.axis2.async.AsyncMessageReceiver に関連付けする必要があります (リスト 4)。

リスト 4. メッセージ受信プログラムを IN-OUT の MEP 変数に関連付ける
<messageReceivers>
    // other message receivers 
   <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" class=
      "org.apache.axis2.async.AsyncMessageReceiver"/>
</messageReceivers>

サーバー・サイドの非同期動作を有効にするためには WS-Addressing を使う必要があります。これは、レスポンスを送信するために新しいスレッドを作成することで、クライアントに対してサーバーが後でレスポンスを送信することを約束しているからです。ただし、もしリクエストの処理に少し時間がかかる場合には、クライアントが使用したトランスポートがタイムアウトするかもしれません。そのため、同じチャネルで非同期呼び出しを行おうとすることは賢明ではないかもしれません。クライアントが非同期の Web サービスにリクエストを送信する際には、クライアントはサーバーがレスポンスを返送できるように、有効な replyTo エンドポイントも指示する必要があります。

クライアントの観点から見ると、クライアントがこの非同期サービスを (例えば HTTP を使って) 呼び出す場合には、同じトランスポート・チャネルで HTTP 202 を受信します。HTTP 202 はクライアントに対して、リクエストは Web サービスに受け付けられたものの、その処理がまだ完了していないことを伝えます。次にクライアントは、リクエスト・メッセージの中に含まれている replyTo のパスを通してレスポンスを受信します。


まとめ

Web サービスのユーザーや Web サービスの作成やホスティングを行う人達は、非同期呼び出しによって、大きな柔軟性を得ることができます。一般的に利用できる Web サービスが増え、また同時に一層複雑になるにつれ、こうした Web サービスを統合できるアプリケーションが多くなります。最終的なアプリケーションの大部分は結局はユーザーと対話動作を行うため、より良いユーザー・エクスペリエンスを提供することが課題になります。クライアント・サイドとサーバー・サイド両方のさまざまなレベルで提供される、Web サービスの非同期呼び出しは、こうした最近の課題のいくつかを解決する上で役立つはずです。

参考文献

学ぶために

製品や技術を入手するために

  • 皆さんの次期開発プロジェクトを IBM trial software を使って革新してください。ダウンロード、あるいは DVD で入手することができます。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services, Open source
ArticleID=267574
ArticleTitle=Axis2 を使って非同期 Web サービスを開発する
publish-date=10112007