コンテンツへスキップ
ものがたり
戻る

.NET 3.5のWCF Routingを実装しよう

先週末にふと思い立ってやっつけでWCF 4.0 Routingを実装したので、ざっくりとした話を書こうと思う。ちなみにまだ完成してはいない。configurationまわりは途中までしか作っていない。内部動作の説明は例によってMonoのものであって.NETのものは知らない。

WCF Routingは、クライアントが直接サービスを呼び出す方式ではなく、クライアントが”router”となるサービスにリクエストを発行すると、routerがそのときの気分やら設定やらによって、対応するサービスにリクエストを転送し、その結果をクライアントに返す、というものだ。そのルーティングの規則は、MessageFilterと、それに対応するServiceEndpointによって決まる1

Usage

ルーティングはRoutingServiceというクラスが実装している。ルーティングのServiceContractとなるインターフェースは、セッションの方式に合わせて4種類(ISimplexDatagramRouter, ISimplexSessionRouter, IDuplexSessionRouter, IRequestReplyRouter)あって、RoutingServiceはこれを全て実装している。

これらのインターフェースのOperationContractとなるメソッドは、いずれもMessage型の引数をとるもので、具体的なクライアント・サーバ間のメッセージのコントラクトは関知しない。

ルーティング テーブルの設定は、RoutingConfigurationというクラスで定義されていて、このクラスのインスタンスを、RoutingBehaviorというIServiceBehaviorの実装クラスがメンバとして保持している。RoutingConfigurationには、前述のMessageFilterとServiceEndpointのテーブルなどが含まれる。

WCF Routingを使う人は、RoutingServiceのホストを用意して(ServiceHostやASP.NETなど)、そのbehaviorとしてRoutingBehaviorを追加する(そこには自分でMessageFilterとServiceEndpointの設定を加えたRoutingConfigurationを渡してやる)、という手順で、ルーティング サービスを公開できる。コードで書くとこんな感じだ:

var host = new ServiceHost (typeof (RoutingService));  

host.AddServiceEndpoint (  

    typeof (IRequestReplyRouter),  

    new BasicHttpBinding (),  

    new Uri ("http://localhost:8081/router"));  

var config = new RoutingConfiguration ();  

var clientEndpoint = new ServiceEndpoint (  

    ContractDescription.GetContract (typeof (IRequestReplyRouter)),  

    new BasicHttpBinding (),  

    new EndpointAddress ("http://localhost:8080/service"));  

var list = new List ();  

list.Add (clientEndpoint);  

config.FilterTable.Add (new MatchAllMessageFilter (), list);  

host.Description.Behaviors.Add (new RoutingBehavior (config));

Internals

RoutingBehaviorがServiceHostにApplyDispatchBehavior()によって適用されると、まずRoutingExtensionというクラスがServiceHostBaseのIExtensionとしてアタッチされる。そして、このRoutingExtensionに、RoutingBehaviorに割り当てられたRoutingConfigurationがApplyConfiguration()というメソッドを通じて設定される。

そして、もしRoutingConfigurationにおいてSOAPエンベロープのすげ替えがSoapProcessingEnabledプロパティによって有効になっていたら(これがデフォルト)、RoutingConfigurationに含まれる全てのServiceEndpointに、IEndpointBehaviorの実装であるSoapProcessingBehaviorが追加される。このbehaviorは、後でルーティングされるMessageのMessageVersionを調整する(クライアントからrouterへの送信に使われたMessageVersionから、実際のサービスで要求されるMessageVersionにすげ替えたり、その逆を復路で行ったりする)のに用いられる。ユーザがコードで使用する必要は無い。

RoutingConfigurationが適用されると、このRoutingExtensionは、内部的にRoutingServiceのインスタンス化を制御するIInstanceProviderをデフォルトのものからすげ替えて、サービスメソッド呼び出し時に生成される新しいRoutingServiceインスタンスに、内部的にRoutingConfigurationが渡されるようにする。

RoutingServiceが生成され、その(セッション方式によって異なるServiceContractインターフェースとなっている)サービスメソッドが呼び出されるたびに、ルーティングのクライアントがMessageFilterTable`2に設定されたServiceEndpointから生成され、実際のサービスに向けてリクエストが発行される。この時、先に説明したSoapProcessingBehaviorが、Messageに手を加えて、SOAPヘッダの内容などを置き換える(たとえば、クライアントから送られてくるToヘッダの宛先はrouter自身だが、これはサービスのEndpointAddressに置き換える必要がある)。これはWCFにおけるIClientMessageInspectorの仕組みを活用して、このインターフェースの実装クラスとして行われる。

ちなみにSoapProcessingBehaviorのインターフェースであるIEndpointBehaviorというのは厄介なもので、ChannelFactoryを使用しないと、ApplyClientBehavior()を適用される場面が無い。これが呼び出されないと、IClientMessageInspectorを設定する機会が無いので、転送に必要なSOAPメッセージの改変が行われなくなってしまう。最初は、Bindingから自前でIChannelFactoryを生成した方が、チャネルの管理は行いやすいので、そうしていたのだが、それでは上手くいかないことに気付いて少し悩んだ。

Remaining Topics

ルーティングは、Request-Reply方式のメッセージだけでなく、simplex, duplexのメッセージにも適用できる。そして、それらについては、複数の転送先を指示することもできる(request-replyの場合はreplyの処理がある関係で1つのみである)。これについては詳しくは書かない。

configurationについても、まだ実装が完了していないので割愛する。

最後にひとつだけおいしい(?)話を書いておこう。.NET 4.0のWCF Routingは.NET 4.0でしか使えないが、MonoのWCF Routingは.NET 3.5があれば使うことが出来る。mcs/class/System.ServiceModel.Routingディレクトリでmakeすれば、2.0ランタイム用のSystem.ServiceModel.Routing.dllの出来上がりだ。まあ、ちゃんと動くかどうかは分からないけど。

というわけで、タイトルは釣りだけど単なる釣りではないのでした。

Footnotes

  1. MessageFilterTable<IEnumerable>というややこしい型の変数に格納される


この記事を共有:

前の記事
Eric Schmidt「に」反論する人々が不思議でしょうがない
次の記事
リリースラッシュになりそう