特集
次世代XML Webサービスを試す Part 3
SOAPメッセージのルーティング(後編)

4.WSEによるルータの実装

インフォテリア株式会社
吉松 史彰
2003/02/27

Page1 Page2 Page3 Page4 Page5 Page6


Back Issue
1
Web Services Enhancements イントロダクション −
2
WS-Security詳細解説
3
SOAPメッセージのルーティング(前編)

サーバ側での経路指定

 前半では、XML Webサービスの呼び出しの際に、クライアント側で設定したルート情報によりSOAPメッセージのルーティングを行う実装を紹介した。しかしこの実装では、クライアント側でサーバ側の経路情報を把握しておかなければならず、ファイアウォールやロード・バランサのようなルータは実現できない。

 WSEでは、サーバ側で経路を決めて、WS-Routingのヘッダを設定することもできるようになっている。というより、現実的にはこちらの方がよく使われることになるだろう。WSEでは、サーバ側で経路を決定する方法が2種類ある。1つはルータを自分で実装する方法で、もう1つはWS-Referral仕様に基づくリファラル・キャッシュを参照する方法だ。

ルータの実装

 WSEでは、自分でルータを実装してルーティングを行うことを「Content-based Routing」と呼んでいる。メッセージの内容に応じてルートを決定するからだ。これによって、ファイアウォールやロード・バランサのようなルーティングを行うことができる。ルータを自分で実装するには、次の手順を踏めばよい。ここでは以下の図のようなルートで呼び出されるXML Webサービスと、そのクライアントを作成してみる。

自分でルータを実装して行うルーティング
ルータは、メッセージの内容に応じてルートを決定することができる。

Microsoft.Web.Services.Routing.RoutingHandlerクラスを継承する独自クラスを作成して、以前の連載ですでに作成済みのWebサービス・プロジェクト「WSERouting」に追加する。ここではクラスの名前を「CapriciousRouter」とした。

public class CapriciousRouter :
    Microsoft.Web.Services.Routing.RoutingHandler {
  ……
}
Microsoft.Web.Services.Routing.RoutingHandlerクラスを継承した独自のCapriciousRouterクラス

RoutingHandlerクラスのProcessRequestMessageメソッドをオーバーライドする。

 次にロード・バランシングやセキュリティ機構などの独自のルーティング・ロジックを実装する。ここではまずメッセージの内容を調べて、条件に一致するメッセージであれば3つのルータからランダムに1つ選び、フォワード・パスに追加している。3つのルータはそれぞれ単に最終的な受信者にメッセージを転送する。

protected override void ProcessRequestMessage(
                    SoapEnvelope message, Path outgoingPath) {
  XmlNamespaceManager mgr
                    = new XmlNamespaceManager(new NameTable());
  mgr.AddNamespace("ns1", "http://tempuri.org/");

  if (message.Body.SelectSingleNode("//ns1:input", mgr)
                          .FirstChild.Value.Equals("hello")) {
    Random d = new Random();
    int i = d.Next(Int32.MaxValue);
    Uri next = new Uri(String.Format(
                    "http://localhost/WSERouting/{0}.ashx",
                    Convert.ToChar((i % 3) + 'a')));
    outgoingPath.Fwd.Insert(0, new Via(next));
  } else
    base.ProcessRequestMessage(message, outgoingPath);
}
オーバーライドしたProcessRequestMessageメソッド
このメソッドで独自のルーティング・ロジックを実装できる。ここでは例として、ランダムな経路を1つ選択するルーティングを実装した。

Web.configファイルを編集し、次のエントリをsystem.web要素の中のhttpHandlers要素に追加する。

 これによって、「router.ashx」へのアクセスに対して手順で作成したクラスが呼び出されることになる。このとき、すでに記述してある「*.ashx」のエントリより上に記述しなければならないことに注意してほしい拡張子が同じであるため、*.ashxが先に見付かってしまうと、手順で記述したコードは実行されなくなってしまう。

<add type="WSERouting.CapriciousRouter, WSERouting"
                              path="router.ashx" verb="*" />
Web.configファイルでのHTTPハンドラの追加
これにより、「router.ashx」へのアクセスに対して、で作成したロジックが呼び出されるようになる。

Service1.asmx.csのコードを書き換えて、どのルータが処理したか分かるようにする。

 次のコードで、Timestamp要素のReceived子要素から処理したノードを取得できる。

[WebMethod]
public string Echo(string input) {
  SoapContext ctx = HttpSoapContext.RequestContext;
  return input + " Processed by "
               + ctx.Timestamp.Receivers[1].Actor.AbsoluteUri;
}
Webサービス・メソッドの修正
Webサービスが返す文字列に、ルーティングを処理したノードのURLを含める。

 これでサーバ側ができたので、クライアント側のコードも変更する。クライアント側では、取りあえず自分から直接送信する相手として、最終的な受信者への到達方法を知っているルータを指定すればよいことになる。

static void Main(string[] args) {
  localhost.Service1 svc = new localhost.Service1();
  SoapContext ctx = svc.RequestSoapContext;
  ctx.Path.Fwd.Add(new
      Via(new Uri("http://localhost/WSERouting/router.ashx")));
  Console.WriteLine(svc.Echo("hello"));
}
修正したWebサービス・クライアント
クライアントでは、上記で作成したルータを最初の経路として設定し、Webサービスの呼び出しを行う。

 このクライアント・アプリケーションを実行すると、次のような実行結果となる。

Webサービス・クライアントの実行例
この場合、a.ashxを経由してWebサービスが呼び出されていることが分かる。

 この場合、まず次のようなメッセージがrouter.ashxに送信される。to要素にはService1.asmxが設定されている。またfwd/via要素にはrouter.ashxしか設定されていないことに注意してほしい。

<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Header>
    <wsrp:path
        soap:actor="http://schemas.xmlsoap.org/soap/actor/next"
        soap:mustUnderstand="1"
        xmlns:wsrp="http://schemas.xmlsoap.org/rp">
      <wsrp:action>http://tempuri.org/Echo</wsrp:action>
      <wsrp:to>http://localhost/WSERouting/Service1.asmx</wsrp:to>
      <wsrp:fwd>
        <wsrp:via>
          http://localhost/WSERouting/router.ashx
        </wsrp:via>
      </wsrp:fwd>
      <wsrp:id>uuid:57839732-131e-4eb2-a7cb-bd36328895cc</wsrp:id>
    </wsrp:path>
    <wsu:Timestamp
        xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
      <wsu:Created>2003-02-01T11:02:32Z</wsu:Created>
      <wsu:Expires>2003-02-01T11:07:32Z</wsu:Expires>
    </wsu:Timestamp>
  </soap:Header>
  <soap:Body>
    <Echo xmlns="http://tempuri.org/">
      <input>hello</input>
    </Echo>
  </soap:Body>
</soap:Envelope>
クライアントからrouter.ashxに送信されるSOAPメッセージ
最終的な受信者を示すto要素にService1.asmxが設定される。経路上の中継者を示すfwd/via要素にはrouter.ashxしか設定されていない。

 router.ashx(実体はCapriciousRouter)は上記のメッセージを受け取って中身を確認し、次のようなメッセージを生成して選択されたルータに転送する。fwd/via要素に新しくa.ashxが付け加わっている。これはrouter.ashxが選択した次の経路を意味する。

<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Header>
    <wsu:Timestamp
        xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
      <wsu:Created>2003-02-01T11:02:32Z</wsu:Created>
      <wsu:Expires>2003-02-01T11:07:32Z</wsu:Expires>
      <wsu:Received Actor="http://localhost/WSERouting/router.ashx"
          Delay="691">
        2003-02-01T11:02:42Z
      </wsu:Received>
    </wsu:Timestamp>
    <wsrp:path
        soap:actor="http://schemas.xmlsoap.org/soap/actor/next"
        soap:mustUnderstand="1"
        xmlns:wsrp="http://schemas.xmlsoap.org/rp">
      <wsrp:action>http://tempuri.org/Echo</wsrp:action>
      <wsrp:to>http://localhost/WSERouting/Service1.asmx</wsrp:to>
      <wsrp:fwd>
        <wsrp:via>http://localhost/WSERouting/a.ashx</wsrp:via>
      </wsrp:fwd>
      <wsrp:id>uuid:57839732-131e-4eb2-a7cb-bd36328895cc</wsrp:id>
    </wsrp:path>
  </soap:Header>
  <soap:Body>
    <Echo xmlns="http://tempuri.org/">
      <input>hello</input>
    </Echo>
  </soap:Body>
</soap:Envelope>
router.ashxが選択されたルータに転送するSOAPメッセージ
fwd/via要素に新しくa.ashxが付け加わっている。

 a.ashxはメッセージを受け取ると、次のメッセージをService1.asmxに送信する。

<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Header>
    <wsu:Timestamp
        xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
      <wsu:Created>2003-02-01T11:02:32Z</wsu:Created>
      <wsu:Expires>2003-02-01T11:07:32Z</wsu:Expires>
      <wsu:Received Actor="http://localhost/WSERouting/router.ashx"
          Delay="691">
        2003-02-01T11:02:42Z
      </wsu:Received>
      <wsu:Received Actor="http://localhost/WSERouting/a.ashx"
          Delay="781">
        2003-02-01T11:02:42Z
      </wsu:Received>
    </wsu:Timestamp>
    <wsrp:path
        soap:actor="http://schemas.xmlsoap.org/soap/actor/next"
        soap:mustUnderstand="1"
        xmlns:wsrp="http://schemas.xmlsoap.org/rp">
      <wsrp:action>http://tempuri.org/Echo</wsrp:action>
      <wsrp:to>http://localhost/WSERouting/Service1.asmx</wsrp:to>
      <wsrp:id>uuid:57839732-131e-4eb2-a7cb-bd36328895cc</wsrp:id>
    </wsrp:path>
  </soap:Header>
  <soap:Body>
    <Echo xmlns="http://tempuri.org/">
      <input>hello</input>
    </Echo>
  </soap:Body>
</soap:Envelope>
ルータa.ashxがWebサービスService1.asmxに送信するSOAPメッセージ

 Service1.asmxは自分がto要素に設定されていることを確認して、受け取ったメッセージのBody部分だけ処理し、結果を送信元(ここではa.ashx)に返す。a.ashx(RoutingHandler)は受け取った結果をそのままrouter.ashxに返し、router.ashx(CapliciousRouter)はクライアントに結果を返信する。

<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Header>
    <wsu:Timestamp
        xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
      <wsu:Created>2003-02-01T11:02:43Z</wsu:Created>
      <wsu:Expires>2003-02-01T11:07:43Z</wsu:Expires>
      <wsu:Received Actor="http://localhost/WSERouting/a.ashx"
          Delay="123">
        2003-02-01T11:02:44Z
      </wsu:Received>
      <wsu:Received Actor="http://localhost/WSERouting/router.ashx"
          Delay="143">
        2003-02-01T11:02:44Z
      </wsu:Received>
    </wsu:Timestamp>
  </soap:Header>
  <soap:Body>
    <EchoResponse xmlns="http://tempuri.org/">
      <EchoResult>
        hello Processed by http://localhost/WSERouting/a.ashx
      </EchoResult>
    </EchoResponse>
  </soap:Body>
</soap:Envelope>
最終的にクライアントに返されるSOAPメッセージ

 このようにすれば、WSEを利用してサーバ側でメッセージの内容やあて先に応じた経路を設定できるようになる。


 INDEX
  [特集]次世代XML Webサービスを試す Part 3
  SOAPメッセージのルーティング
    1.SOAPメッセージのルーティングを規定するWS-Routing
    2.WSEでのWS-Routing利用
    3.WS-RoutingのSOAPメッセージ
  4.WSEによるルータの実装
    5.WS-Referralを利用したルーティング(1)
    6.WS-Referralを利用したルーティング(2)
 
 「特集:次世代XML Webサービスを試す」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間