EIP / Request-Reply


要求-応答(Request-Reply)

サマリ

  • メッセージングで要求と応答を実現するには、必要なものは、双方向チャネルで双方向のメッセージが必要。
  • 応答を受信するためには、「同期ブロック」「非同期コールバック」の2つのアプローチがある。
  • 要求-応答のでは、「メッセージングRPC」「メッセージングクエリ」「通知/肯定応答」の3種のメッセージがある。

詳細

  • 二つのアプリケーションがメッセージングを介して通信するとき、通信は一方向。アプリケーションは、双方向の会話をしたいかもしれない。アプリケーションがメッセージを送信するとき、それはどのように受信側からの応答を得ることができるか?
  • メッセージングは、アプリケーション間の一方向通信を提供し、メッセージは、送信側から受信側に、一方向にメッセージチャネル上を移動する。この非同期伝送は、配信の信頼性を向上させ、受信機から送信者を疎結合にする。
  • 問題は、コンポーネント間の通信がたびたび双方向である必要があるということ。プログラムは、関数を呼び出すときに、戻り値を受け取る。また、クエリを実行するとき、クエリの結果を受け取る。あるコンポーネントが変更を別へ通知するとき、それが肯定応答を受信することを望んでもよい。

どのようにメッセージングを双方向にすることができるのか?


  • (山下コメント:なんか、送信チャネル内で、送信メッセージ自体を、受信側で書き換えて送信者へ通知する方法について説明してますが、さすがにないよね的に書かれていて、まったくの余談のように見えるので、そのこのあたりの要約は省略しました。)要は→「必要なものは、双方向チャネルで双方向のメッセージです。」っていいたいみたいです。
  • 独自のチャンネルでそれぞれ、Request-Reply(要求 - 応答)メッセージのペアを送信する。
  • 要求 - 応答は2人の参加者がある。
    • リクエスタ:要求メッセージを送信し、応答メッセージを待つ。
    • リプライヤ:要求メッセージを受信し、応答メッセージで応答する。
  • 要求チャネルは、Point-to-Pointチャネルまたはパブリッシュ·サブスクライブのチャネルを指定できる。(違いは、要求はすべての利害関係者にブロードキャストされるべきであるか、または単一のコンシューマによって処理されるべきかどうかということ。)
  • 一方で、応答チャネルは、通常の応答をブロードキャストしても意味がないので、ほとんどの場合にPoint-to-Pointとなる。

要求 - 応答の場合、要求側は、応答を受信するための二つのアプローチがある。


  • 1.同期ブロック:呼び出し側の単一のスレッドは、要求メッセージを送り、応答メッセージを待つためにブロック(コンシューマをポーリングして)してその応答を処理する方法。これは実装するのは簡単だが、要求側がクラッシュした場合、ブロックされたスレッドを困難だが再確立しなければならない。
  • 2.非同期コールバック:呼び出し側の1つのスレッドは、要求メッセージを送信し、そして応答のためのコールバックを設定する。別のスレッドでは、応答メッセージを待機する。そして、応答メッセージが到着すると、応答スレッドは、呼び出し元のコンテキストを再確立し、応答を処理し、適切なコールバックを呼び出す。このアプローチは、単一の応答チャネルと、複数のリクエストスレッドの応答を処理する単一のスレッドを共有することができる。もしリクエスタがクラッシュした場合、それは単に応答スレッドを再起動することによって回復することができる。しかし追加される複雑さは、呼び出し元のコンテキストを再確立するコールバックメカニズムです。

要求と応答を送信する2つのアプリケーションのメッセージが何を表しているか。


  • 1.メッセージングRPC:メッセージングを使用してリモートプロシージャ呼び出しを実装する方法。リクエストはプライヤーが呼び出すべき関数を記述するCommandMessage?(コマンドメッセージ)。返事は、関数の戻り値または例外を含むDocumentMessage?(文書メッセージ)。
  • 2.メッセージングクエリ:これは、メッセージングを使用して、リモートクエリを実行する方法。要求がクエリを含むコマンドメッセージであり、返信はクエリーの結果(恐らく、メッセージシーケンス)。
  • 3.通知/肯定応答:これは、メッセージングを使用して受信確認のイベント通知を提供する。リクエストは通知を提供するEventMessage?(イベントメッセージ)で、応答は、通知を認めるDocumentMessage?(ドキュメント メッセージ)。受信確認は、それ自体が別の要求、イベントの詳細を求めるものであってもよい。(山下コメント:イベントで送ったものを応答確認しても、送信元は送信先のこと分からない(というかそもそもその疎結合がメリット)ので困ってしまうのでは?)

リクエストは、メソッド呼び出しのようなもの。このように、応答は三つの可能性のいずれかになる。


  • 1.Void:単にメソッドは、呼び出し側が続行できるように終了したことを発信者に通知します。
  • 2.返り値:メソッドの戻り値である単一のオブジェクト。
  • 3.例外:メソッドが正常に完了するまえに打ち切られた理由を示す単一の例外オブジェクト。
  • リクエストはどこに応答を送信するためにプライヤーを伝えるためにリターンアドレスが含まれているべき。また、返信はこの応答があるよう要求する指定した相関IDを含むべきです。

例:SOAP 1.1のメッセージ 例:SOAP 1.2応答メッセージ交換パターン


  • SOAPメッセージは、要求 - 応答ペアになっている。SOAP要求メッセージには、送信者が受信機に呼び出したいサービスを示すのに対し、応答メッセージは、結果値または障害(例外に相当する)が含まれている。
  • SOAP 1.1は、応答メッセージを持っていて、大まかに説明されているのに対し、SOAP 1.2は、明示的なリクエスト - レスポンスメッセージ交換パターンを取り入れている。

例:JMSのリクエスタオブジェクト


  • JMSは、要求 - 応答を実装するために使用できる機能がいくつか含まれる。
  • TemporaryQueue?は、プログラム的に作成され、それを使用する接続と同じくらい長持ちすることができるQueue。同じ接続によって作成されたMessageConsumers?だけがキューから読み取ることができるので、効果的な専用の接続になる。リクエスタは、一時キューを作成し、要求メッセージの返信先のプロパティで指定する。プライヤーは、指定されたキューに返信を送る。一時キューを持つ欠点は、接続が閉じたときに、キューとその中のすべてのメッセージが削除されるということで、メッセージングシステムがクラッシュした場合、メッセージは失われる。
  • JMSは、要求を送信し、応答を受信するための単純なクラスとしてQueueRequestor?も提供する。送信側は、要求を送信するためのQueueSender?と、応答を受信するためのQueueReceiver?を抱えている。各リクエスタは応答を受信するための独自の一時キューを作成し、要求のreply-toプロパティにそれを指定します。(山下コメント:一時キューを作成するの?何のためのQueueReceiver??)

QueueConnection? connection = // obtain the connection

Queue requestQueue = // obtain the queue

Message request = // create the request message

QueueSession? session = connection.createQueueSession?(false, Session.AUTO_ACKNOWLEDGE);

QueueRequestor? requestor = new QueueRequestor?(session, requestQueue );

Message reply = requestor.request(request);

  • QueueRequestor?によって使用されるTemporaryQueue?は、ポイントツーポイントチャネル。そのパブリッシュ·サブスクライブチャンネルはTemporaryTopic?TopicRequestor?に相当する。

  • 関連パターン:コマンドメッセージ、相関識別子、文書メッセージ、リモートプロシージャ呼び出し、イベントメッセージ、保証された配信、メッセージ、メッセージチャネル、メッセージシーケンス、メッセージング、ポイントツーポイントチャネル、ポーリングコンシューマ、パブリッシュ·サブスクライブチャンネル、リターンアドレス