EIP / Message Router


メッセージ経路(Message Router)

一言要約

Message Routerは、Message Router 周りのコンポーネントがMessage Router の存在を知らなくて済み、新しい処理を影響少なく追加可能。 だが、乱用し過ぎるとパフォーマンス劣化につながるのでよく考えて導入すること。

要約

Pipes and Filters のつながりの中での複数の処理の流れは、Message Channels によってつなげられている。


メッセージを、条件の設定によって異なるフィルターに通すことができるように どうやって、個々の処理の流れを引き離すことができるか?



Pipes and Filters のアーキテクチャの方式は、フィルターを決められたパイプでお互いを、直に接続する。 これは、意味がとおっている。なぜなら、Pipes and Filters パターンの多くのアプリケーションが、 同じ順序の処理の流れを通るデータ項目の組み合わせに基づいているからである。 例えば、コンパイラは、いつも最初に字句解析を実行し、次に構文解析を行い、最後に意味解析をする。


一方、メッセージに基づいた統合ソリューションは、大きなデータの組み合わせと、一緒でなくていい個々のメッセージを扱う。 結果として、個々のメッセージは、異なる一連の処理の流れを最も必要とすることになる。


Message Channelは、メッセージの送り手と受け手を引き離す。 また、これは複数のアプリケーションが、Message Channelに、Messages を発行することができることを意味する。 結果として、Message Channelは、 メッセージの種類や他の基準に基づいて別に扱わなければならないかもしれない異なるソースから メッセージを含めることができる。


各メッセージタイプ別に、Message Channel を分けることができるだろう。 (コンセプトについては、Datatype Channelで、後でより詳しく説明されてある) そして、それぞれのチャンネルを要求された処理のステップにメッセージタイプで接続する。


しかし、これは、正しいチャンネルにメッセージを発行するために、 異なる処理のステップの選択基準を知る時にメッセージの発信元を必要とするだろう。 また、Message Channelの数が急増することにつながっていくだろう。 さらに、受けるメッセージの流れの決定は、単にメッセージの発信元によらないかもしれない。


例えば、今までのところ、チャンネルを通過してきたメッセージの数によって、 メッセージの目的地が変わる状況を想像できるだろう。 したがって、1つではない発信元がメッセージの数を知っているかもしれないし、 メッセージを正しいチャンネルに送ることができないだろう。


Message Channels は、ルーティング機能のとても基本的なフォームを提供する。 アプリケーションが、Message ChannelMessage を発行し、Messgae の目的地の情報以上のものを持たない。 したがって、Message のパスは、コンポーネントがMessge Channelに登録するコンポーネントによって 変更することができる。 しかし、このルーティングの種類は、個々のメッセージの属性を考慮しない。


1度、コンポーネントをMessage Channelに登録すると、全てのメッセージをそのチャンネルから、 個々のメッセージの特定の属性にかかわらず、初期設定で使ってしまうだろう。 この振る舞いは、テキストファイルを処理するのにUNIXでパイプの記号を使うことに似ている。 Pipes and Filters チェーンへの処理を作成することができるが、 チェーンのライフタイムによって、テキストの全てのラインが、同じステップを通ってしまう。


共通のMessage Channelに到着するメッセージを処理すべきかどうかを決定する 責任をもった受けてのコンポーネントをつくるだろう。 これには問題があって、一旦、メッセージを使ってしまうと、コンポーネントがメッセージを必要ない と判断してしまうからだ。 それで、簡単に調べるための他のコンポーネントのチャンネルに、メッセージを戻すことができない。 いくつかのメッセージングシステムは、チャンネルからメッセージを消さないで、 受け手がメッセージの属性を調べることができる。そのため、メッセージを使うかどうかを判断することができる。


しかし、これは一般的な解決策ではなく、また、特定のメッセージの種類で使用するコンポーネントを絞られることになる。 なぜなら、メッセージを選択するロジックがコンポーネントによって組立られているからである。 これは、コンポーネントを再利用する見込みを減らすことになるだろう。 また、Pipes and Filters モデルの主要な強みである構成力をなくしてしまうだろう。


多くの代替手段は、私たちの必要に応じて追加しているコンポーネントを修正することだと思う。 しかし、たいていの統合ソリューションでは、コンポーネントの組立てることは、ほとんどのケースで あらゆる事例で修正することができない大きなアプリケーションとなる。 なぜなら、パッケージ化されたアプリケーション、もしくは、レガシーアプリケーションだからだ。


これは、メッセージングシステムやもしくは他のアプリケーションの要求に応じて、 メッセージの生成や消費するアプリケーションを調整することさえも不可能にしてしまい無駄が多くなる。


Pipes and Filters の1つのメリットが、個々のコンポネートのComposabilityである。 この特性によって、既に存在しているコンポーネントを変更せずに 追加の処理をフィルターチェーンに登録することができる。 これは、それらと次に実行するステップがなにかを判断する他のフィルターとの間に登録することで、 2つのフィルターを引き離すためのオプションを作っている。



特別なフィルターである"Message Rotuter"を登録し、1つのMessage Channelから"Message""を使う。 そして、設定されている条件によって、異なるMessage Channelに再公開する。



Message Router は、複数の出力チャンネルに接続する点において''Pipes and Filters'の基本的な考え方とは違う。 しかし、Pipes and Filtersのアーキテクチャのおかげで、Message Router の周りのコンポーネントは、 完全に Message Routerの存在を知らない。 これらは、1つのチャンネルで単純にメッセージを使い、他にメッセージを発行する。 Message Routerの定義しているプロパティは、メッセージの内容を修正せず、 メッセージの目的地だけに関連している。


Message Routerを使うキーとなる利点は、メッセージの目的地のための判断基準が、 1つの場所で維持されていることである。 もし新しいメッセージの種類が定義されていたら、全ての他のコンポーネントには影響を与えることなく、 新しい処理のコンポーネントが追加され、ルーティングのルールが変更される。 私たちはMessage Routerのロジックだけを変更すればいい。 また、全てのメッセージが、1つのMessage Routerを通過する間、 受信するメッセージは正しい順番で1つずつ処理されてることが保証されている。


Message Routerの意図としては、それぞれからフィルターを引き離すことであるが、 Message Routerを使うことで、実際には逆の効果を引き起こすことになる。 Message Router コンポーネントは、正しいチャンネルにメッセージを送信する可能性のある 全ての目的地を知っていなければならない。 もし可能性のある目的地リストが頻繁に変更されるなら、 Message Routerは、メンテナンスのボトルネックに変わってしまう。


個々の受信先が、関心のあるメッセージを判断するように、 Publish-Subscribe ChannelMessage Fitersの配列を使うことによって実現することができる。
これら2つの代替手段を、"予測できるルーティング"と、"反応できるフィルタリング"と呼んでおり、私たちは対比している。 (より詳細な比較については、Chapter 7 のMessage Fileter,"Message Router"を参照)


なぜかというと、Message Routerが、追加の処理ステップの登録を要求することによってパフォーマンスは劣化する。 多くのメッセージベースのシステムは、他のチャンネルに位置づける前に、 1つのチャンネルからメッセージをデコードしなければならない。 そのため、コンピュータのオーバーヘッドにつながるのである。 このオーバーヘッドは、Message Routerをパフォーマンスのボトルネックへと変える。 並行して複数のルーターを使う、もしくは、ハードウェアを追加することによって、 この影響は最小化することができる。 結果として、メッセージのスループットには影響はないかもしれないが、レイテンシーはほぼ確実に増加する。


ほとんどの良いツールでは、Message Routerを多用している。 よく考えたMessage Routerの利用は、疎結合のメリットをデメリットに変える。 疎結合のシステムは、システムを通る全体のメッセージの流れであるソリューションの"大きな絵"を理解するには難しくする。 このことはメッセージジングのソリューションの共通の問題である。 そして、ルータの利用は、問題を悪化させる。


もし全てが全てにおいて疎結合であるなら実際に流れているメッセージの方向を理解できなくなる。 これは、テスト、デバッグ、メンテナンスを複雑にする。多くのツールは、問題を軽減するのに役立つ。


最初に、私たちは、実行時にメッセージを調べるためにMessage History を利用する。 そして、それらが横断したコンポーネントかどうか見る。あるいはまた、私たちは、システムが登録もしくは公開している各コンポーネントに対しての全てのチャンネルのリストをコンパイルする。


この知識は、コンポーネントを横断する全てのありえるメッセージの流れの図を描くのと同じで、 このタイプの静的な分析を簡単に行えるようにするために、多くのEAIパッケージは、チャンネル予約情報を、中央リポジトリで維持管理している。

Message Router Variants

Message Router は、受信メッセージによって、出力するチャンネルを判断するするためにいくつかの基準を利用する。ほとんどの些細なケースでは、ルータを修正する。


このケースでは、1つの入力チャンネルと、1つの出力チャンネル、だけを定義する。 修正されたルータは、入力チャンネルから1つのメッセージを使い、出力チャンネルに発行する。 どうしてこのような無能なルータを使うのだろうか?


修正したルータは、意図的にサブシステムを引き離すのに役立つかもしれない。そのため、私たちは後でより有能なルータを登録できる。つまりは、複数の統合ソリューションの間でメッセージを関連付けることができるかもしれないのだ。 ほとんどのケースでは、修正したルータを、メッセージの中身を変形させる、もしくは、異なるチャンネルタイプをまたいでメッセージを送信するために、Message Translater か、Channel Adapterと連結させるだろう。


多くのMessage Routerは、メッセージの目的地をメッセージ自身のプロパティだけで判断する。 例えば、メッセージタイプや、特定のメッセージフィールドの値、である。 私たちはこのルータを、Content-Base Router と呼ぶ。 このルータのタイプは共通であるため、Content-Based Routerパターンではより詳細に特徴を述べている。


このルータは、ロードバランシング、テスト、フェイルオーバーを機能させるために、共通で使われていた。 例えば、もしコンポーネントの処理が失敗したら、context-based routerは、 メッセージを他のコンポーネントの処理へと再度ルーティングする、このようなフェイルオーバー能力を提供する。 他のルータが、ロードバランサーに似た並行した処理を実現するために複数のチャンネルを均等に横断するようメッセージの流れを分割する。


いくつかのMessage Channelは、Message Routerの利用なしで基本的なロードバランシグ機能を既に提供している。 なぜなら複数のCompeting Consumersは、早く使用できる同一のチャンネルからメッセージをそれぞれ使用するからである。 しかしMessage Routerは、チャンネルで実装された単純なラウンドロビンとは対象的に、 追加で内蔵でメッセージをルーティングするための自動制御をもたせることができる。


多くのMessage Routerは、ステートレスである。 言い換えると、ルーティングを決めるときには1つのメッセージだけ見る。 ルーティングが決まった時に、他のルータが前のメッセージの中身を考慮する。 例えば、Pipes and Filtersの例は、既に受け付けた全てのメッセージのリストを保持し続けることで メッセージの重複をなくしたルータを使っている。 これらのはルータは、ステートフルである。


ほとんどのMessage Routerは、ルーティングを決めるためにハードコードしたロジックを含んでいる。 しかし、いくつかの形ではControl Busに接続する。 そのため、ミドルウェアのソリューションはいくつかのコードを作らなければならならいこと、 もしくは、メッセージの流れへの割り込むこと、を除いて判断基準を変更することができる。


例えば、Control Busは、システムの中の全てのMessage Routerに、 グローバル変数の値を伝搬することができる。 これは、メッセージングシステムを製品モードのテストへ切り替えるための テストを行うのにとても役に立つ。 Dynamic Routerは、それぞれの潜在的な受取人からメッセージを制御することを ベースとして、動的に自身を設定される。


Chapter 7, "Message Routing"では、より変わったMessage Routerを紹介する


例:商用のEAI ツール

Message Router の考えは、Message Broker のコンセプトが中心である。 全ての商用のEAIツールのほとんどに実装されている。 これらのツールは、Messageの受け入れ、検証し、変換し、正しい送り先に送る。 このアーキテクチャは、Message Brokerがアプリケーション間の取りまとめをするので、 他のアプリケーションに一切注意しなければいけないことで、共有するアプリケーションを軽減してくれる。 これは、企業統合ではキーとなる機能である。 接続されるほとんどのアプリケーションは、パッケージもしくは、 レガシーアプリケーションで、統合したときには利用者に負担をかけないようにしなければならない。 したがって、アプリケーションを変更しないようにするためにミドルウェアは全ての経路選択のロジックに含まれていなければならない。 Message Brokerは、GoFで発表されているMediator と同等の統合である。



例:Simple Router With C# and MSMQ


このコード例は、とても単純なルータを紹介する。 受け取ったメッセージを単純な条件とした2つの可能な出力チャンネルの1つに経路選択する。


class SimpleRouter
{
    protected MessageQueue inQueue;
    protected MessageQueue outQueue1;
    protected MessageQueue outQueue2;

    public SimpleRouter (MessageQueue inQueue, MessageQueue outQueue1, MessageQueue outQueue2)
    {
      this.inQueue = inQueue;
      this.outQueue1 = outQueue1;
      this.outQueue2 = outQueue2;

      inQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(OnMessage);
      inQueue.BeginReceive();
    }
    
    private void OnMessage(Object source, ReceiveCompletedEventArgs asyncResult)
    {
      MsseageQueue mq = (MessageQueue)source;
      Message message = mq.EndReceive(asyncResult.AsyncResult);
      if( IsConditionFulfilled() )
         outQueue1.Send(message);
      else
         outQueue2.Send(message);
      mq.BeginReceive();
    }

    protected bool toggle = false;
    protected bool IsConditionFulfilled()
    {
      toggle = !toggle;
      return toggle;
    }
} 
     
     


  • Pipes and Filtersで表現していた単純なフィルタのようで、Simple Routerクラスは、C#のデリゲートを使っているメッセージのEvent-Driven Consumer を実装している。
  • コンストラクタは、inQueueで到着しているメッセージのためのハンドラーにOnMessage?メソッドを登録している。これは、.NET Frameworkが、inQueueに到着するすべてのメッセージに対して、OnMessage?メソッドを起動するためである。
  • OnMessage?は、IsConditionFulfilled?メソッドが呼び出されて、メッセージをどこの経路にするかを把握する。
  • IsConditionFulfilled?は単純に2つのチャンネルを切り替え、メッセージのシーケンスを均一にoutQueue1とoutQueue2の間で分割していく。
  • コードを最小限におさえるために、この単純なルータはトランザクションではない。
  • 入力チャンネルからメッセージを使用した後にもしルータがこわれたら。また出力チャンネルに発行する前にメッセージがなくなったら。の話である
  • Transactional Client では、トランザクションのエンドポイントをどうやって作るかを説明する。

担当者のコメント

  • 手直しの意味も踏まえ、全訳にしました。(8/19)

みんなの突っ込み

  • 7/31に見てますが、現状のverは文とか抜粋箇所とか若干手直しした方がいいかも?(渡邉)
  • 杉野さんから出た、「Publish-Subscribe Channel + Message Filter」と「Message Router」はどう違うのか、という疑問への回答に近い記述がありましたね。Kindleなのでページ数わかりませんが、Message Router Variantsのちょい上らへん