キューの概要
ここでは、キューを使った通信の概要とその基本概念を解説します。 後の各セクションでは、キューの概念がどのように Windows Communication Foundation (WCF) に活かされているかを詳しく説明します。
キューの基本概念
分散アプリケーションの設計では、サービスとクライアントとの間の通信に適したトランスポートを選択することが重要になります。 使用するトランスポートの種類の決定には、いくつかの要素が影響します。 そのうちの 1 つである、サービス、クライアント、トランスポートの分離という重要な要素により、キューを使用するトランスポートと、TCP や HTTP などの直接的なトランスポートのどちらを使用するかが決定します。 TCP や HTTP などの直接的なトランスポートの性質により、サービスやクライアントの機能が停止したり、ネットワークに障害が発生したりすると、通信全体が停止します。 アプリケーションが全体として正常に機能するためには、サービス、クライアント、ネットワークがすべて揃って稼働している必要があります。 キューを使用するトランスポートでは、分離が実現されます。つまり、サービスやクライアントが停止したり、これらの間の通信リンクに障害が発生したりしても、障害が発生していないサービスやクライアントは引き続き機能します。
キューとは、通信ノードやネットワークの障害時にも通信の信頼性を確保するための手段です。 通信ノード間で交換されたメッセージは、いったんキューに取り込まれ、それから配信されます。 通常、キューはその過程で、記憶装置に保存されます。これは、一時的に記憶されるだけの場合も、ある程度の期間保存される場合もあります。 キューは、クライアントからのメッセージをサービスに代わって保存しておき、後でサービスに転送します。 このように、仲介の役割を果たすキューにより、いずれかの通信ノードで生じた障害が分離されるので、システムの可用性およびサービスの分離を実現するには、キューを使用するトランスポートが適しています。 しかしその一方で、通信の遅延が大きい、という欠点があります。 "遅延" とは、クライアントがメッセージを送信してから、サービスが受け取るまでの時間のことです。 遅延が大きいと、メッセージを送ってから実際に処理されるまでに、時間がかかります。 キューを使用する多くのアプリケーションでは、この遅延への対策が講じられています。 次の図は、キューに置かれた通信の概念モデルを表したものです。
キューを使う通信の概念モデル
現実には、キューの概念は分散処理モデルに基づいています。 キューはサービスまたはクライアントと同じホスト上に配置される場合も、この両方から独立している場合もあります。 一般的には、キューはサービスと同じホスト上に配置します。 このような構成の場合、クライアント側では、リモート キューに接続していつでも使用できると想定することはできません。 同様に、キューを使用するサービスの可用性に関係なく、キューの可用性が確保されなければなりません。 そこで、キューの管理がキュー マネージャーによって行われます。 キュー マネージャーは、他のキュー マネージャーからキューに送信されたメッセージを受け取ります。 さらに、リモート キューとの接続を管理し、これらのリモート キューにメッセージを転送するのもキュー マネージャーの役割です。 クライアントやサービスに障害が発生してもキューの可用性を確保できるように、キュー マネージャーは通常、外部サービスとして実行されます。
クライアントがメッセージをキューに送信すると、そのメッセージがターゲット キュー (サービス側のキュー マネージャーが管理するキュー) に送信されます。 クライアント側のキュー マネージャーは、メッセージを転送キュー (送信キュー) に送ります。 転送キューとは、クライアントのキュー マネージャーが管理するキューであり、ここではターゲット キューに転送されるメッセージがいったん蓄積されます。 次にキュー マネージャーは、ターゲット キューを管理するキュー マネージャーとの通信経路を検索し、転送します。 通信の信頼性を確保するために、キュー マネージャーには、データの損失を防止する転送プロトコルが実装されています。 送信先のキュー マネージャーは、ターゲット キュー宛てのメッセージを受信してターゲット キューに格納します。 サービスがターゲット キューからの読み取り要求を行うと、キュー マネージャーから目的のアプリケーションにメッセージが配信されます。 以上のやり取りに関与する 4 者の関係を図に示します。
キューを使った通信 - 標準的な構成
このように、キュー マネージャーによりサービスとクライアントが分離されているため、送信側または受信側のいずれかが停止していても、実際の通信には影響しません。 キューの導入によるもう 1 つの利点として、複数のアプリケーションが同じキューからメッセージを読み込めるため、各ノードが個別に通信する場合に比べ、スループットを改善できるという点があります。 実際、拡張性やスループットを改善するためにキューを使用するケースも少なくありません。
キューとトランザクション
トランザクションでは、複数の操作をグループ化できるため、トランザクションに含まれる操作が 1 つでも失敗すると、すべての処理が失敗となります。 例として、ATM を使用して 10 万円を普通預金から当座預金に移すことを考えてみましょう。 これは、次の操作を伴います。
普通預金から 10 万円を引き出す。
当座預金に 10 万円を預ける。
最初の処理が成功して 10 万円が引き出された後、2 番目の操作が失敗すれば、既に引き出された 10 万円が失われてしまいます。 このような状況を避けて口座の状態を有効に維持するには、いずれか一方の操作に失敗したら、両方の操作を失敗と見なす必要があります。
トランザクション メッセージでは、メッセージをキューに送る操作やキューから読み出す操作を、トランザクション内で行えます。 つまり、トランザクション内でメッセージをキューに送った後でそのトランザクションがロールバックされると、その結果は、始めからメッセージを送らなかった場合と同じになります。 同様に、トランザクション内でメッセージを受け取った後でそのトランザクションがロールバックされると、その結果は、メッセージを受信しなかった場合と同じになります。 メッセージはキューに残り、読み出せる状態になります。
待ち時間が長いため、メッセージを送る時点では、ターゲット キューに到達するまでどれぐらいかかるか、サービスがそのメッセージを処理するのにどれぐらいかかるかを知る手段はありません。 したがって、単一のトランザクションを使用してメッセージの送信、受信、処理を行うことは、望ましくありません。 トランザクション全体がコミットされるまでの時間が予測できないからです。 トランザクションを使用してクライアントとサービスがキューを介して通信する場合は、クライアントとサービスでそれぞれトランザクションが必要になります。 下の図は、キューを使った典型的な通信でのトランザクション境界を示しています。
キューを使った通信 - 送受信の別個のトランザクション
クライアントのトランザクションでは、メッセージを処理し、送信します。 トランザクションがコミットされると、メッセージは転送キューに入ります。 一方、サービスでは、ターゲット キューからメッセージを読み取り、メッセージを処理して、トランザクションをコミットします。 メッセージの処理中にエラーが発生すると、そのメッセージはロールバックされ、ターゲット キューに置かれます。
キューを使った非同期通信
キューを利用すると非同期通信も可能です。 キューを使用してメッセージを送信するアプリケーションは、キュー マネージャーで生じる遅延が大きいため、メッセージが受信側で受信および処理されるまで待機することができません。 想定以上に長い期間、メッセージがキューに残ったままになる可能性があるからです。 この状況を回避するために、メッセージに有効期間 (TTL: Time-To-Live) を設定できるようになっています。 これはメッセージが転送キューに留まっていられる時間を表す値です。 この時間が経過してもターゲット キューに送信されなかったメッセージは、配信不能キューに転送することができます。
送信側がメッセージを送信した時点で、送信操作から制御が戻っても、送信側の転送キューにメッセージが到達したことを示しているにすぎません。 ターゲット キューへの転送に失敗していても、送信元アプリケーションで、すぐにそれを知ることはできないのです。 このような失敗を記録するために、失敗したメッセージは配信不能キューに転送されます。
メッセージがターゲット キューに到達できなかったり有効期間を経過したりして発生したエラーは、個別に処理する必要があります。 このため、キューを使用するアプリケーションでは、次の 2 つに分けてロジックを記述することも少なくありません。
正常時にクライアントおよびサービスがメッセージを送受信するためのロジック
失敗した転送や配信からのメッセージを処理するための補正ロジック
以下のセクションで、これらの概念について説明します。
配信不能キューのプログラミング
配信不能キューには、さまざまな原因によりターゲット キューに到達できなかったメッセージが入っています。 原因としては、有効期限の経過や、ターゲット キューとの接続経路の異常などが考えられます。
通常、アプリケーションは、システム全体の配信不能キューを読み取り、エラー原因を調べたうえで、エラー状態を修復して再送したり、エラーを記録したりするなどの適切な処理を行うことができます。
有害メッセージ キューのプログラミング
メッセージがターゲット キューに到達しても、サービス側で繰り返し処理に失敗する場合があります。 原因としては、アプリケーションがトランザクション内でキューからメッセージを読み込み、データベースを更新しようとしたところ、データベースの接続が一時的に切断されていた、というような状況が考えられます。 この場合は、トランザクションのロールバック、新たなトランザクションの作成、メッセージの再読み込みが行われます。 ただし、2 回目の試行で成功するかどうかはわかりません。 エラーの原因によっては、メッセージをアプリケーションに配信する処理が繰り返し失敗することがあります。 このようなメッセージは "有害" であると見なされ、有害メッセージ キューに移されます。このキューの内容は、有害メッセージを処理するアプリケーションに渡すことができます。