次の方法で共有


基礎

Service Bus のルーター

JUVAL LOWY

私が担当した前 2 回のコラムでは、インターネット接続の問題と関連するセキュリティの問題という、.NET Service Bus がその設計上で対処する中核となる問題について説明しました。しかし、Service Bus には、単に接続やメッセージ リレーに留まらない多くの機能があります。今回のコラムでは、まずサービス レジストリについて説明してから、ルーターの各機能について説明します。さらに次回は、クラウドでのキューについて説明する予定です。この 2 回のコラムを通じて、新しい強力なデザイン パターン、ルーターとキューを組み合わせる方法、および全体的な操作効率を向上するヘルパー クラスとツールについて説明します。

サービス レジストリ

.NET Service Bus は、ソリューションのベース アドレスまたはそのサブ URI の 1 つでリッスンするサービスの ATOM フィードを提供します。この ATOM フィードは、ブラウザーを使用してソリューション (または URI) に移動することで表示できます。このフィードは、ソリューション内のサービスや各機能 (ルーターやキューなど) のレジストリおよびディレクトリとして機能します。

既定では、ブラウザーでサービスを表示することはできません。サービス レジストリのパブリッシュについては、エンドポイント ビヘイビア クラスの ServiceRegistrySettings を使用して制御します。このクラスは次のように定義します。

public enum DiscoveryType
{
Public,
Private
}
public class ServiceRegistrySettings : IEndpointBehavior
{
public ServiceRegistrySettings();
public ServiceRegistrySettings(DiscoveryType discoveryType);
public DiscoveryType DiscoveryMode
{get;set;}
public string DisplayName
{get;set;}
}

ホストは、このレジストリにパブリッシュするすべてのエンドポイントにこのビヘイビアをプログラムから追加する必要があります (これに対応する構成可能なビヘイビアはありません)。たとえば、すべてのエンドポイントをレジストリにパブリッシュするには、次のコードを使用します。

IEndpointBehavior registeryBehavior =
new ServiceRegistrySettings(DiscoveryType.Public);
ServiceHost host = new ServiceHost(typeof(MyService));
foreach(ServiceEndpoint endpoint in host.Description.Endpoints)
{
endpoint.Behaviors.Add(registeryBehavior);
}
host.Open();


図 1 Services Bus Explorer

Service Bus を目に見えるようにするために、Service Bus Explorer を開発しました (図 1 参照)。このツールは、調査するソリューションの名前を受け取り、フィードにログオンしてから、実行中のサービスを表示します。このエクスプローラーは、ATOM フィードを解析して、各項目を左側のツリーに表示するだけです。複数のソリューションを調査でき、右側のウィンドウにはソリューション管理ページが表示されます。また、利用可能なルーターとキューも表示され、これらを管理する場合に非常に有効です。

インターセプターとしてのクラウド

実際、Service Bus はもともと、Web をまたいだ呼び出しの接続に関する重大な問題を解決するために開発されました。しかし、それ以外にもさまざまな可能性があります。基本となる Windows Communication Foundation (WCF) アーキテクチャと Service Bus のアーキテクチャを比較してみましょう。どちらのアーキテクチャでも、クライアントがサービスと直接通信することはなく、呼び出しはミドルウェアによってインターセプトされます。通常の WCF の場合、このミドルウェアがプロキシで、インターセプト チェーンがサービスにつながります (図 2 参照)。

呼び出しがリレーされる場合、このミドルウェアは通常の WCF ミドルウェアと Service Bus 自体で構成されます (図 3 参照)。アーキテクチャ上は、呼び出しをインターセプトして付加価値を提供するという点で、同じデザインです。現在リリースされている Service Bus での付加価値は、ルーターとキューをインストールできることです。実際、Service Bus は、優れたインターセプターとして機能する可能性が高く、さらに多くの付加価値がいずれ提供されるようになるでしょう。


図 2 通常の WCF 呼び出しのインターセプト


図 3 インターセプターとしてのクラウド

ルーターとしての接合ポイント

Service Bus では、ソリューションに含まれるすべての URI が、実際にアドレス指定できるメッセージ接合ポイントです。クライアントからメッセージをこの接合ポイントに送信し、接合ポイントはメッセージをサービスにリレーできます。ただし、各接合ポイントは、ルーターとしても機能できます。複数のサービスからそのルーターをサブスクライブでき、ルーターは同じクライアント メッセージをサブスクライブするすべてのサービスに転送することも、1 つのサービスのみに転送することもできます (図 4 参照)。

クライアントはルーターの背後にあるサービスから分離されます。そのため、クライアントに関する限り、ルーターの背後にサービスが存在していてもいなくてもかまいません。すべてのクライアントが認識する必要があるのは、メッセージの送信先となるルーターの場所だけです。クライアントは、ルーターに適切な配布ポリシーが構成されていることを前提に動作し、配布についてはルーターに任せます。このような間接処理のため、メッセージはおのずと一方向になります。実際 WFC では、クライアントのバインディングと、ルーターをサブスクライブしているサービスのバインディングの双方に、一方向リレー バインディングが使用されることが要件になります。クライアントでは、サービスから結果を受け取ることも、サービス側エラーの通知を受け取ることもありません。このことは、バインディングが一方向でなかったとしても、当てはまります。クライアント メッセージが多重化されて複数のサービスに送られる場合、この操作から結果 (どのサービスからの結果か不明) やエラー (どのサービスのエラーか不明) が返されても意味がないことは明らかです。また、サービスがクライアントにコール バックすることもありません。


図 4 ルーターとしての Services Bus

ルーター ポリシー

ソリューションの管理者は、ルーターをサービスとは切り離して管理します。各ルーターには、ルーターの動作と有効期間を定めるポリシーが必要です。既定では、ソリューション管理者は、プログラムから呼び出しを実行して、ルーターを作成および管理する必要があります。メッセージ接合ポイントのすべてのポリシーは、抽象クラスの JunctionPolicy から派生します (図 5)。

接合ポイントの Discoverability プロパティは、DiscoverabilityPolicy 型の列挙値です。このプロパティは、接合ポイントがソリューションの ATOM フィードに含まれるかどうかを制御します。Discoverability プロパティの既定値は DiscoverabilityPolicy.Managers で、プライベート ポリシーを意味します。このプロパティを DiscoverabilityPolicy.Public に設定すると、接合ポイントが ATOM フィードにパブリッシュされます。ExpirationInstant プロパティは、接合ポイントの有効期間を制御します。ポリシーの有効期間が過ぎると、接合ポイントは取り除かれます。ExpirationInstant プロパティの既定値は 1 日です。明らかに、これは実際のルーター (またはキュー) にとって適切な場合もそうでない場合もあります。したがって、通常は、実際の状況に合った値に設定して、監視する必要があります。接合ポイントの有効期間が残り少なくなり、接合ポイントがまだ使用中である場合は有効期間を更新します。私はこれを接合ポイントの "リース時間" の更新と呼び、リース時間を延長するものを "スポンサー" と呼んでいます。また、TransportProtection プロパティは、接合ポイントに送られるメッセージの転送セキュリティを制御します。未加工の WCF メッセージをメッセージ接合ポイントに送信したり、接合ポイントから取得したりするときは、TransportProtectionPolicy.AllPaths という値が設定された転送セキュリティを使用するように制限されます。この値は、すべての接合ポイント ポリシーの既定値であり、他の値に設定しないことをお勧めします。

図 5 JunctionPolicy クラス

public enum DiscoverabilityPolicy
{
Managers,Public //More values
}
public enum TransportProtectionPolicy
{
None,AllPaths
}
[DataContract]
public abstract class JunctionPolicy
{
public JunctionPolicy();
public JunctionPolicy(JunctionPolicy policyToCopy);
public DateTime ExpirationInstant
{get;set;}
public DiscoverabilityPolicy Discoverability
{get;set;}
public TransportProtectionPolicy TransportProtection
{get;set;}
//More members
}

図 6 RouterPolicy クラス

public enum MessageDistributionPolicy
{
AllSubscribers,
OneSubscriber
}
[DataContract]
public class RouterPolicy : JunctionPolicy,...
{
public RouterPolicy();
public RouterPolicy(RouterPolicy otherRouterPolicy);
public int MaxSubscribers
{get;set;}
public MessageDistributionPolicy MessageDistribution
{get;set;}
//More members
}

各ルーター ポリシーは、図 6 の定義のように、RouterPolicy クラスにより表現されます。ここで中心になる 2 つのプロパティは、MaxSubscribers と MessageDistribution です。名前が示すように、MaxSubscribers は、ルーターに同時接続できるサブスクライバーの最大数です。既定値は 1 で、最大値は 50 です。ルーターへの同時接続数が最大に達すると、それ以降にサブスクライブを試みたサービスにはエラーが返されます。MessageDistribution プロパティは MessageDistributionPolicy 型の列挙値で、既定値は MessageDistributionPolicy.OneSubscriber です。これは、ルーターをサブスクライブしているサービスの 1 つにしか、メッセージが渡されないことを意味します。値を MessageDistributionPolicy.AllSubscribers に設定すると、メッセージはすべてのサービスに配信されます。

ルーター ポリシーの管理

ルーター ポリシーを管理する場合は、次の RouterManagementClient クラスを使用できます。

public static class RouterManagementClient
{
public static RouterClient CreateRouter(
TransportClientEndpointBehavior credential,
Uri routerUri,RouterPolicy policy);
public static void DeleteRouter(
TransportClientEndpointBehavior credential,
Uri routerUri);
public static RouterClient GetRouter(
TransportClientEndpointBehavior credential,
Uri routerUri);
public static RouterPolicy GetRouterPolicy(
TransportClientEndpointBehavior credential,
Uri routerUri);
public static DateTime RenewRouter(
TransportClientEndpointBehavior credential,
Uri routerUri,TimeSpan requestedExpiration);
}

RouterManagementClient は静的クラスで、このクラスのすべてのメンバーは (TransportClientEndpointBehavior 型の) 資格情報オブジェクトを必要とします。この型については前回のコラムで説明しています (msdn.microsoft.com/magazine/dd942847.aspx 参照)。次のコードでは、簡単なルーターとポリシーを作成しています。

Uri routerAddress =
new Uri(@"sb://MySolution.servicebus.windows.net/MyRouter/");
TransportClientEndpointBehavior credential = ...;
RouterPolicy policy = new RouterPolicy();
policy.ExpirationInstant = DateTime.UtcNow + TimeSpan.FromMinutes(5);
policy.MaxSubscribers = 4;
policy.MessageDistribution = MessageDistributionPolicy.AllSubscribers;
RouterManagementClient.CreateRouter(credential,routerAddress,policy);

この例では、ルーター ポリシー オブジェクトのインスタンスを作成し、ポリシーの値を設定しています。ポリシーでは、ルーターをサブスクライブしているすべてのサービスにメッセージを配布すること、ルーターでは最大 4 つまでしかサブスクライバーを受け付けないことなどを指定します。このルーターには、5 分間という短い有効期間が構成されています。ルーターをインストールするには、ポリシーといくつかの有効な資格情報を指定して RouterManagementClient の CreateRouter メソッドを呼び出すだけです。

プログラムからの呼び出しに代わる手段として、Services Bus Explorer を使用してルーターを表示し、変更できます。アドレスと各種のポリシー プロパティを指定することで、新しいルーターを作成できます。同様に、ソリューション内のすべてのルーターを削除することもできます。

ソリューション ツリーで既存のルーターのポリシーを選択し、右側のウィンドウでプロパティを操作することで、既存のルーターのポリシーも表示および変更できます (図 7 参照)。

クライアントは、アドレスがルーターのアドレスであるプロキシを作成して、このプロキシを呼び出すだけで、メッセージをルーターに送信できます。

ルーターのサブスクライブ

サービスがルーターからメッセージを受信するには、まず、ルーターのアドレスを持つエンドポイントを構成し、そのエンドポイントをホストに追加して、ルーターをサブスクライブする必要があります。この方法の 1 つは、RouterClient ヘルパー クラスを使用することで、次のように定義します。

public sealed class RouterClient
{
public ServiceEndpoint AddRouterServiceEndpoint<T>(
ServiceHost serviceHost);
//More members
}

ルーターを作成または取得する RouterManagementClient のメソッドは、RouterClient のインスタンスを返します。サービス ホストのインスタンスを指定して AddRouterServiceEndpoint<T> メソッドを呼び出す必要があります。また、(プログラムからまたは構成ファイルを使用して) ルーターのアドレスをそのアドレスとして持つエンドポイントをホストに追加します。以下に例を示します。

<service name = "MyService">
<endpoint
address = "sb://MySolution.servicebus.windows.net/MyRouter/"
binding = "netOnewayRelayBinding"
contract = "..."
/>
</service>


図 7 Services Bus Explorer に表示したルーター


図 8 ルーターからルーターへのサブスクライブ

ルーターの使用

ルーターには 3 つの用途があります。1 つ目は、同じメッセージを複数のサービスにブロードキャストすることです。定番の例としては、もちろん、ルーターをサブスクライブしているサービスにイベントを発行することで、Service Bus をイベントハブ ミドルウェアとして扱います。4 月号のコラム (msdn.microsoft.com/ja-jp/magazine/dd569756.aspx) でも触れたように、イベントを発行することには額面どおり以上の価値があります。したがって、イベントについての話は後にまわし、このコラムの最後で取り上げます。

ルーターの 2 つ目の用途は、サービス間の負荷を調整するロード バランサーとしての役割です。メッセージを 1 つのサブスクライバーにのみ配布するようにルーター ポリシーを構成していれば、そのルーターは、実質的にそのルーターをサブスクライブしているサービス間のロード バランサーとして機能します。どのロード バランサーでも、なんらかのアルゴリズムに従って、次のメッセージを処理するサービスを決定します。よく使用されるアルゴリズムには、ラウンド ロビン、ランダム、なんらかの公正なスコアボード、キューなどがあります。私のテストでは、Service Bus は擬似ラウンド ロビンを使用しているようです。つまり、約 95% で公正なラウンド ロビンが使用されています。負荷分散ルーターの作成を簡素化するため、私が作成した ServiceBusHelper ヘルパー クラスでは、次に示すように、CreateLoadBalancingRouter メソッドに多くのオーバーロードを用意しています。

public static partial class ServiceBusHelper
{
//Uses CardSpace for credential
public static void CreateLoadBalancingRouter(string balancerAddress);
/* Additional CreateLoadBalancingRouter(...)
with different credentials */
}

CreateLoadBalancingRouter メソッドは、メッセージを 1 つのサブスクライバーにのみ配布するポリシーを使用するルーターを作成します。また、ルーターを作成する前にそのルーターが存在するかどうかを検出し、ルーターのリースを自動的に更新します。CreateLoadBalancingRouter の実装は、このコラムのコード ダウンロードに含まれています。

すべての CreateLoadBalancingRouter パブリック メソッドは、さまざまな種類の資格情報を受け取り (私が提供する実装では CardSpace を使用しています)、TransportClientEndpointBehavior のインスタンスを作成して、内部でオーバーロードされた CreateLoadBalancingRouter メソッドを呼び出します。オーバーロードされたバージョンは、ルーターを ATOM フィードにもパブリッシュし、特定のポリシーと資格情報を指定して CreateRouter を呼び出す負荷分散ポリシーを作成します。CreateRouter メソッドは、まず RouterExists メソッドを使用して、ルーターが既に作成されているかどうかをチェックします。残念ながら、このチェックを行う唯一の方法は、ルーターのポリシーを取得しようとしたときに、エラーが発生するかどうかを確認することです。

ルーターが存在していれば、そのルーターに対応する RouterClient を返します。ルーターが存在していなければ、まずスポンサーとして、リースの有効期間切れを監視するためのタイマーをラムダ式を使用して作成します。リース時間の終わりに近づいてきたら、タイマーはラムダ式を呼び出してリースを更新します。その後、ルーターを作成します。

ルーターからルーターへ

ルーターの 3 つ目の用途は、他のルーター (またはキュー) など、他の接合ポイントにメッセージをルーティングすることです。あるルーターから別のルーターをサブスクライブするには、次のように、RouterClient の SubscribeToRouter メソッドを使用します。

public sealed class RouterClient
{
public RouterSubscriptionClient SubscribeToRouter(
RouterClient routerClient,
TimeSpan requestedTimeout);
//More members
}
[Serializable]
public class RouterSubscriptionClient
{
public DateTime Expires {get;}
public DateTime Renew(TimeSpan requestedExpiration,
TransportClientEndpointBehavior credential);
public void Unsubscribe(TransportClientEndpointBehavior credential);
//More members
}

SubscribeToRouter は、サブスクライブ先のルーターのルーター クライアントを受け取り、RouterSubscriptionClient のインスタンスを返します。RouterSubscriptionClient の主な用途は、サブスクリプションの中止と、サブスクリプションのリース時間の更新です。

たとえば、サービス A、サービス B、およびサービス C があるとします。すべてのクライアント呼び出しは、必ずサービス A と、サービス B かサービス C のどちらかに配布される必要があるとします (たとえば、サービス B とサービス C の間で負荷を分散する必要があり、さらに、ログブックとなるサービス A にすべての呼び出しを記録する必要がある場合です)。これまで説明したルーター ポリシーは、すべてのサブスクライバーに配布するか 1 つのサブスクライバーのみに配布するポリシーであるため、不適切です。ただし、2 つのルーターを使用することで、ここでの要件は簡単に満たすことができます (図 8 参照)。

クライアントは、すべてのサービスに配布するポリシーを持つ最上位ルーターにメッセージを送信します。サービス A はこの最上位ルーターをサブスクライブします。また、1 つのサブスクライバーにのみ配布するポリシーを使用するサブルーターも、この最上位ルーターをサブスクライブします。サービス B とサービス C は、このサブルーターをサブスクライブします。図 9 は、必要なルーター構成と、サブルーターから最上位ルーターをサブスクライブする方法を示しています。

サブスクライブしているルーターにサービスをサブスクライブすることについては、最上位ルーターをサブスクライブする場合と異なる重要な点が 1 つあります。サービス エンドポイントは、メッセージの宛先がたとえ最上位ルーターであっても、サブスクライブしているルーターからメッセージを受け取ることを示す必要があります。それには、エンドポイントがリッスンしている URI プロパティをサブスクライブしているサービスに設定し、サービス エンドポイント自体は最上位ルーターのアドレスに設定します。図 9 のアドレスを使用して、次のように、サービス B とサービス C の両方のエンドポイントを定義する必要があるでしょう。

<service name = "MyService">
<endpoint listenUri =
"sb://MySolution.servicebus.windows.net/MySubRouter/"
address = "sb://MySolution.servicebus.windows.net/MyTopRouter/"
binding = "netOnewayRelayBinding"
contract = "..."
/>
</service>

この設定は、次のサービス ホスト拡張機能を使用して自動化できます。

public static partial class ServiceBusHelper
{
public static void SubscribeToRouter(this ServiceHost host,
string subRouterAddress)
{
for(int index = 0;
index < host.Description.Endpoints.Count;index++)
{
host.Description.Endpoints[index].ListenUri =
new Uri(subRouterAddress);
}
}
//More members
}

1 つのルーターが複数のルーターをサブスクライブできることから、サブスクライブしているルーターをサブスクライブしているサービスは、どのアドレス (つまり、どの最上位ルーター) からメッセージを受信するかを十分に気にかける必要があります。

最上位ルーターを削除した場合や、サブスクリプションを中止した場合、サブルーターはクライアントからのメッセージを受信できなくなります。これは、ソリューション管理者にとっては 1 つの機能のようなものです。この手順を使用すれば、ホストを停止しなくても、事実上サービスをブロックできます。

図 9 ルーターからルーターへのサブスクライブ

Uri topRouterAddress =
new Uri(@”sb://MySolution.servicebus.windows.net/MyTopRouter/");
Uri subRouterAddress =
new Uri(@”sb://MySolution.servicebus.windows.net/MySubRouter/");
TransportClientEndpointBehavior credential = ...;
RouterPolicy topRouterPolicy = new RouterPolicy();
subRouterPolicy.MaxSubscribers = 20;
topRouterPolicy.MessageDistribution =
MessageDistributionPolicy.AllSubscribers;
RouterClient topRouterClient = RouterManagementClient.CreateRouter(
credential,topRouterAddress,topRouterPolicy);
RouterPolicy subRouterPolicy = new RouterPolicy();
subRouterPolicy.MaxSubscribers = 30;
subRouterPolicy.MessageDistribution =
MessageDistributionPolicy.OneSubscriber;
RouterClient subRouterClient = RouterManagementClient.CreateRouter(
credential,subRouterAddress,subRouterPolicy);
RouterSubscriptionClient subscription = subRouterClient.
SubscribeToRouter(topRouterClient,
TimeSpan.MaxValue);
//Sometime later
subscription.Unsubscribe(credential);

イベントへ用ルーターの使用

前述のとおり、Service Bus 内のメッセージ接合ポイントは、簡単なイベント ハブとして機能し、サブスクライブしているサービスにメッセージをブロードキャストできます。サブスクライバーの数を最大限にし、すべてのサブスクライバーに通知するルーター ポリシーをインストールする必要があります。この方法の欠点は、イベント バインドを使用する場合の欠点と似ています (これについては、4 月号のコラム msdn.microsoft.com/ja-jp/magazine/dd569756.aspx で説明しています)。操作単位にサブスクライブすることはできないため、あるサブスクライバーにとっては関係のないイベント (単にエンドポイントが一致するだけのイベント) であっても、必ずすべてのサブスクライバーがすべてのイベントを受け取ります。

これを解決するには、すべてのイベントを処理するルーターを 1 つだけ作成するのではなく、イベントごとにルーターを作成します。ある特定のイベントに関心があるサービスは、そのイベント固有のルーターをサブスクライブします。イベント バインドと同様に、イベントごとにルーターを用意するには、各サブスクライバーが操作単位にホスト インスタンスを管理するようにします。これは、1 ホストですべてのルーターを監視する場合、そのホストはイベントを振り分ける処理を何も行わないためです。サブスクライバーは、イベントをサブスクライブするため、またはイベントのサブスクライブを中止するために、各イベント専用のホストを開閉する必要があります。これには、冗長で繰り返しが多いコードが必要です。

さいわい、前回のコラムで紹介したヘルパー クラス EventsRelayHost が、既にこの処理に対応しています。EventsHost という共通基本クラスを少しリファクタリングすると、イベント リレー バインドではなく、ルーターに使用できるようになります (ルーターの場合は、一方向のリレー バインドのみが必要です)。

まず、次のように、EventsHost 抽象基本クラスを定義しました。

//Partial listing
public abstract class EventsHost
{
public EventsHost(Type serviceType,string baseAddress);
public EventsHost(Type serviceType,string[] baseAddresses);
public abstract void SetBinding(NetOnewayRelayBinding binding);
public abstract void SetBinding(string bindingConfigName)
protected abstract NetOnewayRelayBinding GetBinding();
public void Subscribe();
public void Subscribe(Type contractType);
public void Subscribe(Type contractType,string operation)
public void Unsubscribe();
public void Unsubscribe(Type contractType);
public void Unsubscribe(Type contractType,string operation);
}

2 つのサブクラス EventsRelayHost と EventsRouterHost は、図 10 で定義されています。EventsHost and EventsRelayHost の実装については、前回のコラムで紹介しています。

図 10 EventsRelayHost の定義

//For use with the events binding, see previous column
public class EventsRelayHost : EventsHost
{...}
public class EventsRouterHost : EventsHost
{
public override void SetBinding(NetOnewayRelayBinding binding)
{
RelayBinding = binding;
}
public override void SetBinding(string bindingConfigName)
{
SetBinding(new NetOnewayRelayBinding(bindingConfigName));
}
protected override NetOnewayRelayBinding GetBinding()
{
return RelayBinding ?? new NetOnewayRelayBinding();
}
}

EventsRouterHost と EventsRelayHost は似ています。どちらもイベント単位にホストを管理し、監視対象のアドレスがイベント エンドポイントのアドレスであるか、ルーターのアドレスであるかを問題にしません。唯一の違いは、EventsRelayHost が使用するイベント バインドとは異なり、EventsRouterHost ではイベントの受信に一方向のリレー バインドを使用する必要があることです。それ以外は、EventsRouterHost と EventsRelayHost は同じです。これは、どちらも EventsHost から派生します。ホスト管理の実際の処理を行うのは、この EventsHost です。EventsRouterHost も EventsRelayHost も、バインド管理メソッドをオーバーライドするだけです。

それ以外は、EventsRelayHost では、ホストを開閉する代わりに Subscribe 関数と Unsubscribe 関数が使用されるだけで、EventsRouterHost は EventsRelayHost とまったく同じように使用できます。

EventsHost host = new EventsRouterHost(typeof(MyService),
"sb://MySolution.servicebus.windows.net/...");
host.Subscribe();
...
host.Unsubscribe(typeof(IMyEvents),"OnEvent2");

前回のコードでは、特定のイベントのみを監視する専用のホストを内部で開閉しています。

イベント ルーターの作成

EventsRouterHost を使用する場合、ソリューション管理者があらかじめルーターを構成し、EventsRouterHost によってルーターが作成されないようにする必要があります。ルーターとルーターのポリシーの構成をサブスクライバーから分離するために、あえてこのような動作にしました。クライアントが認識するのは接合ポイントのアドレスだけで、一方向のリレー バインドでイベントを発生させるには、前回のコラムでイベントの発行に使用したのと同様に EventRelayClientBase を使用できます。

イベント ルーターの作成を簡素化するには、ServiceBusHelper を使用してルーターを作成します。

public static partial class ServiceBusHelper
{
//Uses CardSpace for credential
public static RouterClient[] CreateEventRouters(string baseAddress,
Type contractType);
/* Additional CreateEventRouters(...) with different credentials */
}

CreateEventRouters はルーターのベース アドレスを受け取ります。EventRelayClientBase はこのベース アドレスにコントラクト名と操作名を追加したアドレスで特定のルーターを開きます。たとえば、次のベース アドレスを受け取ったとします。

sb://MySolution.servicebus.windows.net/

このとき、コントラクトの定義は次のようになっています。

[ServiceContract]
interface IMyEvents
{
[OperationContract(IsOneWay = true)]
void OnEvent1();
[OperationContract(IsOneWay = true)]
void OnEvent2(int number);
[OperationContract(IsOneWay = true)]
void OnEvent3(int number,string text);
}

この場合、CreateEventRouters は次のルーターを作成します。

sb://MySolution.servicebus.windows.net/IMyEvents/OnEvent1/
sb://MySolution.servicebus.windows.net/IMyEvents/OnEvent2/
sb://MySolution.servicebus.windows.net/IMyEvents/OnEvent3/

これはまさに、EventsRouterHost が必要とするルーターです。このコラムのコード ダウンロードでは、エラー処理と、いくつかのセキュリティがオーバーロードされたメソッドを除いて、ServiceBusHelper.CreateEventRouters の実装の一部を提供しています。

資格情報を受け取らないパブリックの CreateEventRouters は、既定で CardSpace を使用します。この場合、TransportClientEndpointBehavior オブジェクトを作成して、これを内部メソッドに渡します。このメソッドは、リフレクションを使用して、特定のサービス コントラクト型の操作のコレクションを取得します。操作ごとに、CreateEventRouter メソッドを呼び出して、これにルーターのアドレスとポリシーを渡します。各ルーターのポリシーは、サブスクライバー数を最大にし、イベントをすべてのサブスクライバーに配布し、ルーターを ATOM フィードにパブリッシュするように構成されています。

ルーターとイベントバインドの比較

クラウドベースの "パブリッシュ - サブスクライブ" ソリューションでは、ほとんどの場合、イベント バインドをお勧めします。まず、サブスクライバーの最大数の制限が大きな難点と考えられます。アプリケーションによっては 50 で十分すぎる場合もありますが、やがて、さらに多くのサブスクライバーをサポートしなければならなくなるとしたらどうでしょう。イベント バインドには、このような制限はありません。ルーターを使用する方法のその他の問題としては、ルーターをあらかじめ作成する必要があることと、リースのスポンサーを気にかける必要があることです。一方、イベント バインドには、このような要件はなく、簡単なアドホック ルーターとして機能します。

それでも、ルーターベースのイベント管理にはメリットが 2 つあります。1 つは、システム管理者が Services Bus Explorer などのツールを使用して、しかもルーターを管理することで間接的にサブスクライバーを管理できることです。2 つ目はさらに重要な点で、ルーターとキューを組み合わせてキュー付きのパブリッシャーを作成し、サブスクライバーをキューに保持できることです。この方法については、次回のコラムで説明したいと思います。

Juval Lowy は、WCF のトレーニングとアーキテクチャ コンサルティングを行う IDesign 社に所属するソフトウェア アーキテクトです。最新の著書は、『Programming WCF Services 2ndEdition』(O'Reilly、2008 年) です。彼は、シリコン バレー地域の Microsoft Regional Director も務めています。連絡先は idesign.net です。