EIP / Dynamic Router


Dynamic Router

ひとこと要約

動的にルーティングの設定を変えられるルーター

要約

複数の送信先間のメッセージを振り分けるのに、Message Routerを使える。

どのように効率よく管理しながらすべての送信先に対するルータの依存性を排除できる?

適切な送信先に直接メッセージをルーティングできるので、Message Routerは非常に効率的

  • 他の解決策、特にリアクティブなフィルタリングはトライアンドエラーなので非効率
  • Routing Slipはそれぞれのメッセージを最初に送信可能なところにルーティングする
  • 送信先が正しければ受け入れられ、正しくなければ次のところへ送られる
  • Message Filterベースのアプローチは、要否に関わらずすべての宛先に送る

分散ルーティングは複数のメッセージ受信者がいるのかあるいは全くいないのか、というリスクにさらされる(意味不明)

  • どちらの状況も中央のルーターを使わない限りは、知ることができない

これを正確に執り行うのに、Message Routerを使う必要がある

  • それぞれの送信先やルーティングのルールについての知識を取り込んだものになる
  • でも、送信先リストが頻繁に変わるならメンテナンスが大変なことに

送信先を共有する特別な設定用メッセージに基づいて自己設定できるようなDynamic Routerを使う

DynamicRouter.gif

通常使われる入力と出力のチャネルに加え、Dynamic Routerには制御チャネルがある

  • 受信者が特別なメッセージを制御チャネルを通してDynamic Routerに送る
  • 自分が存在していることや扱えるメッセージのリストなど
  • Dynamic Routerはそれぞれの受信者用の設定をルールベース内に保存する
  • メッセージが届くと、Dynamic Routerはすべてのルールとルートを評価し、ルールを満たす受信者にメッセージを送信する
  •  これにより、Dynamic Routerがそれぞれの受信者への依存をメンテすることなく、効率的で予測的なルーティングが可能となる

最も基本的なシナリオとしては、それぞれの関係者がその存在とルーティングの設定をDynamic Routerの起動時に通知するもの

  • それぞれの関係者がDynamic Routerに使用される制御キューを知っている必要がある
  • Dynamic Routerがルールを持続的に保持する必要もある
  • でないと再起動に失敗して、ルーティングルールをリカバーできなくなる
  • あるいはブロードキャストメッセージを送りつけ、制御メッセージをリプライするよう仕向けることもできる

制御チャネルを拡張して、関係者が購読と解除のメッセージをDynamic Routerに送れるようにするのは道理にかなう

  • 起動中に受信者が自身をルーティングのスキームに追加したり削除したりできるようになるので

受信者はお互いに独立している

  • Dynamic Routerは複数の受信者が同じタイプのメッセージを求めるといったルールのコンフリクトを扱う必要がある
  • Dynamic Routerはコンフリクトを解消するのにいくつかの異なるやり方がある
  1. すでに存在するメッセージとコンフリクトする制御メッセージを無視する
    • コンフリクトフリーを保証
    • 受信者が起動する順番にルーティングテーブルの状態が依存してしまう
      • 早い者勝ち
    • すべての受信者が同時に起動すると、予測できない振る舞いとなってしまう
  1. 最初に条件にマッチした受信者に送る
    • ルーティングテーブルにコンフリクトを許容するが、メッセージが来ると解決する
  1. 条件にマッチしたすべての受信者に送る
    • コンフリクトに耐性があるが、Dynamic RouterがRecipient Listに変わってしまう
    • Content-Based Routerの振る舞いはそれぞれの入力メッセージに一つの出力メッセージ
      • このやり方はルールに反する

Dynamic Routerの主な欠点

  • ソリューションが複雑
  • 動的な設定のデバッグが困難

Dynamic RouterはローレベルのIPネットワーク機器のようにメッセージベースのミドルウェアが動いている、もう一つの例である

  • 動的なルーティングテーブルがネットワーク間のIPパケットをルーティングするのに使われるのに、非常によく似た動きをする
    • 要はRIP(Routing Information Protocol)に似てる(ということらしい)

Dynamic Routerの一般的な利用例はSOAの動的サービスディスカバリー

  • クライアントアプリケーションがサービスにアクセスしたければ、サービスの名前を服なメッセージをDynamic Routerに送る
  • Dynamic Routerはサービスのディレクトリを維持し、すべてのサービスの名前とチャネルがリスト化される
  • ルーターはそれぞれのサービスプロバイダからの制御メッセージに基づきディレクトリを構築する
  • サービスの要求があると、Dynamic Routerは名前でサービスを検索し、適切なチャネルにメッセージを転送する
  • これにより、クライアントアプリケーションは一つのチャネルに制御メッセージを送るだけでよくなる
    • 特定のサービスプロバイダの特質や場所を知る必要がない
    • プロバイダが変わることさえきにしなくてよい

関連パターン

  • POSAのClient-Dispatcher-Serverパターン
    • クライアントがサービスプロバイダの物理的な位置を知ることなく、特定のサービスにリクエストを送れる

Dynamic Router Using C# and MSMQ

Content-Based Routerの例を拡張

  • 2つのチャネル
    • 入力チャネル inQueue
    • 制御チャネル controlQueue
      • X:QueueName?というフォーマットを使用
 class DynamicRouter {
   protected MessageQueue inQueue; 
   protected MessageQueue controlQueue; 
   protected MessageQueue dunnoQueue;
   protected IDictionary routingTable = (IDictionary)(new Hashtable());
 
   public DynamicRouter(MessageQueue inQueue, MessageQueue controlQueue, MessageQueue dunnoQueue)
   {
     this.inQueue = inQueue; 
     this.controlQueue = controlQueue; 
     this.dunnoQueue = dunnoQueue;
     inQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(OnMessage); 
     inQueue.BeginReceive();
     controlQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(OnControlMessage)
     controlQueue.BeginReceive(); 
   }
 
   protected void OnMessage(Object source, ReceiveCompletedEventArgs asyncResult) 
   {
     MessageQueue mq = (MessageQueue)source;
     mq.Formatter = new System.Messaging.XmlMessageFormatter(new String[] {"System.String,mscorlib"});
     Message message = mq.EndReceive(asyncResult.AsyncResult);
     String key = ((String)message.Body).Substring(0, 1);
     if (routingTable.Contains(key)) 
     {
       MessageQueue destination = (MessageQueue)routingTable[key];
       destination.Send(message); 
     }
     else dunnoQueue.Send(message);
     mq.BeginReceive(); 
   }
   // control message format is X:QueueName as a single string
   protected void OnControlMessage(Object source, ReceiveCompletedEventArgs asyncResult) 
   {
     MessageQueue mq = (MessageQueue)source;
     mq.Formatter = new System.Messaging.XmlMessageFormatter(new String[] {"System.String,mscorlib"}); 
     Message message = mq.EndReceive(asyncResult.AsyncResult);
     String text = ((String)message.Body);
     String [] split = (text.Split(new char[] {':'}, 2)); 
     if (split.Length == 2)
     {
       String key = split[0];
       String queueName = split[1];
       MessageQueue queue = FindQueue(queueName); 
       routingTable.Add(key, queue);
     } else {
       dunnoQueue.Send(message);
     }
     mq.BeginReceive(); 
   }
   protected MessageQueue FindQueue(string queueName) 
   {
     if (!MessageQueue.Exists(queueName)) 
     {
       return MessageQueue.Create(queueName); 
     }
     else
     return new MessageQueue(queueName);
   }
 }

この例ではシンプルなコンフリクト解決メカニズムを使用している

  • 2つの受信者がXで始まるメッセージを受信するとしている場合、二番目の受信者が受信する
    • Hashtableはひとつのキーに一つの値しか保持しないから
  • dunnoQueueは二種類のメッセージを受信
    • ルーティングルールにマッチしないもの
    • 正しいフォーマットに従わない制御メッセージ