EIP / Recipient List


受信者リスト

一言要約

メッセージ内容ごとでなく、受信者ごとにメッセージを送信したい時に、Recipient Listを用いる。

要約

Content-Based Routerはメッセージ内容に基づいて正しいシステムにメッセージをルーティングする。送信者はただチャンネルにメッセージを送信するだけでよく、後はよしなにやってくれる。

ただ、時には受信者を指定したい場合がある。似た概念としてメールの受信者リストがあり、メールメッセージ毎に送信者は受信者リストを指定できる。企業統合の例では、ある機能が1つ以上のプロバイダから実行されるケースがあり、例えば大口注文を行った顧客の信用度を確かめるために複数の信用機関を利用するケースがある。

別の例では、見積もりをとるためにサプライヤに注文メッセージを投げる際、全ベンダーに送信するのではなくユーザ設定に基づいてコントロールしたい場合がある。

どうやって動的な受信者リストにメッセージをルーティングすればよいのか?

これはContent-Based Routerが解決する問題を拡張したもので、同じような力が作用する。

ほとんどのメッセージングシステムはPublish-Subscribe Channelsを提供し、受信者のセットは特定チャネルやsubjectへのsubscriptionに基づいている。しかしながら、アクティブなsubscriberのリストはやや静的でありメッセージごとにコントロールすることは出来ないので、subscriberのリスト別にメッセージを送信できるPublish-Subscribe Channelのようなものが必要なのだが、難しい。

それぞれの潜在的な受信者はMessage FilterSelective Consumerのように内容に従ってフィルタ可能であるが、個々のsubscriberにメッセージを受け取るロジックになりメンテナンスコストが高い。コントロールを一点に集中させるには、メッセージに付与されている受信者を特定することであり、メッセージが全受信者に送信した際に、受信者リストに含まれていない受信者はメッセージを破棄する。

このアプローチの問題は効率性の悪さである(受信者はほとんどのメッセージを破棄する)。設定もまた受信者の一部である「オーナーシステム」に依存しており、特定の受信者から隠さないといけない状況において決して望ましくはない。

また、個々のに受信者に個別にメッセージを発行するメッセージ発信者(originator)が必要となる。しかしその場合はメッセージ発信者上の全受信者に配信の負担を負うことになる。もし発信者がパッケージ化されたアプリケーションの場合、通常は選択できない。多くの場合、統合アプリケーションは統合ソリューションと共有するのを想定していないため、ルーティングロジックを含めるのは現実的ではない。

各受信者用にチャネルを定義する。そしてRecipient Listを使って受信メッセージを検査し、受信者リストを決定し、リスト内の受信者に関連付けられている全チャネルにメッセージを送信する。

Recipient List内に埋め込まれたロジックは2つの別れたパーツで描かれるが実装は一緒に結合していることもある。一番目は受信者リストを処理し、二番目はリストを走査し各受信者に受信メッセージのコピーを送る。Content-Based Routerと同様に、Recipient Listは通常メッセージ内容を変更しない。

Recipient Listは受信者リストを計算する(左)か、別のコンポーネントがリストを供給する(右)

受信者のリストはいくつからのソースから得られる。リスト作成はRecipient Listの外側にあることもあり、メッセージ発信者または別コンポーネントは受信メッセージにリストを添付する。この場合、Recipient Listは既存のリストを順番に処理しなければならない。Recipient Listは送信メッセージのサイズを減らし、誰がリストにいるかが分かるのを防ぐためメッセージからリストを削除する。その代わり、受信メッセージとともに受信者リストを提供するのは、もし個々のメッセージの目的がユーザ選択のような外部要因により動かされる場合は理にかなっている。

ほとんどの場合、Recipient Listはメッセージ内容やRecipient Listに埋め込まれたルールセットに基づいて受信者リストを処理する。これらのルールはハードコードされているか設定可能になっている。

Recipient ListはMessage Routerで述べたような結合に関する同じ問題を抱えている。個々の受信者に予測的にメッセージをルーティングすることは、中心となるコンポーネントが他のコンポーネントの情報を持っている必要があるためコンポーネント間がより密に結合することにつながってしまう。

Recipient Listが情報フローをコントロールするため、受信者が入力チャネルに直接subscribeできないことを確認する必要がある。


堅牢性

Recipient Listコンポーネントは受信者リスト内の指定された受信者ごとに着信メッセージを送信することを担当している。Recipient Listの堅牢な実装は着信メッセージを処理できなければならないが、全送信メッセージが正常に送信された後にのみ「消費」する。このように、Recipient Listコンポーネントは一つのメッセージを受け取る完全な操作と複数のメッセージを送ることはアトミックであることを確認する必要がある。もしRecipient Listが失敗した場合は再起動可能である必要があり、途中で失敗した操作は完了する必要があることを意味する。これは複数の方法で実現される。

単一トランザクション: Recipient Listがトランザクションチャネルを使い、単一トランザクションの一部として送信チャネル上のメッセージを配置する。全メッセージがチャネル上に配置されるまでコミットせず、全メッセージが送信される or されないことを保証する。

永続的受信者リスト: Recipient Listはメッセージを送信済みかを「記憶」できるので、失敗&再起動時に残りの受信者にメッセージを送信できる。受信者リストはディスクやデータベースに保存されているのでRecipient Listがクラッシュしても生存している。

冪等(べきとう: 何回操作しても結果が同じという概念)受信: 別方法として、Recipient Listは単に再起動時に全メッセージを再送信する。これは全ての潜在的受信者は冪等(Idempotent Receiver参照)である必要がある。冪等機能はシステム状態を変更しないので、同じメッセージを2回処理してもコンポーネントの状態は影響を受けない。メッセージが本質的に冪等であるか、または重複メッセージを排除する特別なMessage Filterを挿し込むことで受信コンポーネントを冪等にできる。受信者が受け取ったか疑わしい場合に単に再送信するだけなので冪等性は非常に便利だ。TCP/IPプロトコルは不要なオーバーヘッド無しに信頼性のあるメッセージ配信を保証する似たようなメカニズムを使用している。

動的受信者リスト

Recipient Listはコントロールの維持だけでなく内部に格納されたルールを設定する点においても有用である。ネットワークトラフィックを最小限に抑えるため関係者のみにメッセージを送信したく、メッセージを処理するか否かを受信者毎に決めたい。この機能を実装するために、受信者は特別な制御チャネルを介してRecipient Listにsubscription設定を送信できる。Recipient Listはルールベースで設定を格納し、各メッセージの受信者リストを集めるために利用する。これはメッセージフィルタリング上で制御できるだけでなく配信する際にもRecipient Listの効率性を活かせることができ、動的なRecipient Listを作るにあたりRecipient Listを持ったDynamic Routerの性質も兼ね備えている。

動的受信者リストは制御チャネルを介して受信者により設定される

このアプローチはMessage Filterで述べた価格更新の例で上手く動作するが、冒頭で述べた価格見積の例には適していない(入札に参加するために取得するベンダーを制御したいため)。

ネットワーク効率

全ての受信者に1つのメッセージを送ってメッセージをフィルタするか、各受信者に個別のメッセージを送信するか、どちらが効率的かはメッセージインフラ実装に大きく依存する。一般的に1つのメッセージを持つ受信者が多いとネットワークトラフィックが増大する、と見なせる。例外もあり、publish-subscribeメッセージングシステムのなかにはIPマルチキャスト(Ethernetのバスアーキテクチャを利用)をベースにしており、単一のネットワーク転送で複数の受信者にメッセージをルーティングできる。IPパケットはネットワークを経由して送信されると、同じEthernetセグメント上の全NICはパケットを受信する。通常、NICはパケットの対象受信者を検証し、パケットがNICに関連づけられているIPアドレスに解決されない場合は無視する。マルチキャストルーティングは指定されたマルチキャストグループの一部である全受信者がバスからパケットを読み取るようにする。複数NICで受け取られる単一パケット内容はネットワーク接続に関連付けられたそれぞれのアプリケーションにデータを通す。このアプローチはEthernetバスアーキテクチャに起因するローカルネットワーク上では非常に効率的である。これはポイントツーポイントのTCP/IP接続が必要なインターネット経由では動作しない。一般的に、受信者が離れるとPublish-Subscribe Channelの代わりにRecipient Listを使った方が効率的であると言える。

ブロードキャストのアプローチがRecipient Listより効率的かどうかはネットワークインフラだけでなく受信者数とメッセージ処理数の割合にも依存する。ほとんどの受信者が受信者リストにいる場合は、単にメッセージをブロードキャストし不参加者がメッセージを除外する方が効率的である。しかし、受信者がほんの一部の場合はRecipient Listがより効率的である。

Recipient List 対 Publish-Subscribe & Filters

Recipient Listを用いた予測ルーティングとPublish-Subscribe ChannelMessage Filterの配列を使ったreactiveフィルタリングを使って同じ機能を実装し対比している。判定基準のいくつかはContent-Based RouterMessage Filterの配列との比較したものと同じである。しかしながらRecipient Listの場合、メッセージは複数の受信者に伝達させフィルタオプションをより魅力的にできる。

Recipient List 対 Message Filter配列

以下は2つのソシューションの比較表である。

Recipient ListPublish-Subscribe Channel with Message Filters
中央で制御およびメンテナンス - 予測ルーティング分散したコントロールおよびメンテナンス - reactiveフィルタリング
ルータは参加者を知る必要あり。参加者の追加削除はルータの更新が必要。参加者情報は必要なし。参加者の追加削除は簡単。
ビジネストランザクションに利用(例: 見積依頼)イベント通知や情報メッセージに利用
一般的にキューベースのチャネルに限り効率的publish-subscribeチャネルでより効率的になる。(インフラ依存)

もし複数の受信者にメッセージを送信する場合は、後で結果を調整する必要があるかもしれない。例えば、複数の信用機関にクレジットスコアのリクエストを送信する場合、全結果が返るまで待つ必要があり、結果を比較して正確にクレジットスコアの中央値を計算する。重要度の低い機能は、メッセージスループットを最適化するため最初のレスポンスを受け取れる。これらの戦略は通常Aggregator内に実装される。Scatter-Gatherは単一メッセージで始め複数の受信者に送信し、レスポンスを単一メッセージに結合する状況を説明している。

動的なRecipient ListはメッセージングシステムがPoint-to-Point ChannelsのみでPublish-Subscribe Channelsを提供していない場合、Publish-Subscribe Channelsを実装するために利用する。Recipient Listはトピックにsubscribeされている全Point-to-Point Channelsのリストを維持する。各トピックは一つの特定Recipient Listインスタンスにて表現できる。これは受信者が特定データソースをsubscribeできるような特定条件を適用する必要がある場合に役立つ。ほとんどのPublish-Subscribe Channelsは任意のコンポーネントがチャネルにsubscribeできるようにしているが、Recipient Listはリストにsubscribeする人を制限することでソースデータへアクセス制御するロジックを簡単に実装できる。もちろん、これはメッセージングシステムが受信者に入力チャネルをRecipient Listに直接アクセスしないことが前提である。

例:融資ブローカー

第9章「Interlude: Composed Messaging」の例は要件を満たした銀行にのみ融資見積もり依頼を送るためにRecipient Listを使っている。

例: C#とMSMQの動的受信者リスト

この例はDynamic Routerの例を動的なRecipient Listに変えており、コード構成は非常にている。


担当者のつぶやき

  • なげぇ

みんなの突っ込み