WCF エッセンシャル

WCF アプリケーション開発向けの強力なインスタンス管理技法の発見

Juval Lowy


この記事で取り上げる話題:

  • パーコール サービス
  • パーセッション サービス
  • 共有可能なサービス
  • インスタンスのアクティブ化解除

この記事で使用する技術:

  • Windows Communication Foundation

サンプルコードのダウンロード: InstanceManagement2006_06.exe (230KB)
翻訳元: Discover Mighty Instance Management Techniques For Developing WCF Apps (英語)


目次

  1. パーコール サービス
  2. パーセッション サービス
  3. 共有可能なサービス
  4. プロキシの複製
  5. インスタンスの共有
  6. シングルトン サービス
  7. オペレーションの明確化
  8. インスタンスのアクティブ化解除
  9. スロットリング
  10. スロットリングの構成
  11. スロットルされた値の読み込み
  12. まとめ

インスタンス管理は、Windows Communication Foundation がメッセージ一式をサービス インスタンスにバインドするために使用する一連の技法のことです。これが必要な理由には、拡張性、パフォーマンス、スループット、トランザクション、およびキューされた呼び出しの必要性はアプリケーションによって大きく異なり、さまざまな需要に対処するために 1 つがすべてを満たすような解決法が存在しないことがあります。インスタンス管理を理解することは、拡張可能で整合性のあるサービス指向のアプリケーション開発にとっては欠かせません。この記事では、さまざまなインスタンス管理モードの原理について説明し、インスタンス管理モードをいつ使用するか、およびその使用方法のガイドラインを示し、振る舞い、コンテキスト、オペレーションの明確化、およびインスタンスのアクティブ化解除などの関連するいくつかのテーマについて検討します。

概して、サービス インスタンス モード自体はクライアント サイドで出現すべきではない、厳密にはサービスサイドの実装詳細です。サービス インスタンス モードおよびそれ以外のローカルのサービスサイドの特徴のいくつかをサポートするために、Windows Communication Foundation は振る舞いの概念を定義します。振る舞いとは、通信パターンに影響を与えないローカルのサービス属性です。

ServiceBehaviorAttribute がサービスの振る舞い (サービスのエンドポイントのすべてに影響を与える振る舞い) を構成し、サービス実装クラスに直接適用されます。図 1 に示すように、この属性は、どのインスタンス モードをサービスに使用するかを制御する値を持つ enum 型の InstanceContextMode の InstanceContextMode プロパティを定義します。

図 1 InstanceContextMode

public enum InstanceContextMode
{
    PerCall, PerSession, Shareable, Single
}

[AttributeUsage(AttributeTargets.Class)]
public sealed class ServiceBehaviorAttribute : Attribute, ...
{
    public InstanceContextMode InstanceContextMode {get;set;}
    ... // さらなるメンバ
}

ページのトップへ


1. パーコール サービス

パーコール サービスとは、Windows Communication Foundation の既定のインスタンス化モードです。パーコール アクティブ化向けにサービスの種類が構成されていると、サービス インスタンス、共通言語ランタイム (CLR) オブジェクトはクライアントを呼び出している間のみ存在します。すべてのクライアント要求により、新たな専用サービス インスタンスが取得されます。図 2 は、この単独呼び出しのアクティブ化の動作方法を説明します。

図 2 パーコール インスタンス化
図 2 パーコール インスタンス化

  1. クライアントはプロキシを呼び出し、プロキシはその呼び出しをサービスへ転送します。
  2. Windows Communication Foundation はサービス インスタンスを作成し、そのサービス インスタンスのメソッドを呼び出します。
  3. メソッド呼び出しから戻ってきた後で、もしオブジェクトが IDisposable を実装している場合は、Windows Communication Foundation はそのオブジェクトの IDisposable.Dispose を呼び出します。

典型的なクライアントサーバー プログラミング モデルでは、C++ あるいは C# などの言語を使用して、すべてのクライアントがクライアント専用のサーバー オブジェクトを取得します。この方法の基本的な問題は、基準化がうまくできないことです。サーバー オブジェクトには、データベース接続、通信ポート、あるいはファイルなどのコストがかかる、または不十分なリソースを保持することがあります。多くのクライアントにサービスを提供するアプリケーションを想像してください。一般的には、クライアント アプリケーションが開始して、終了するときにオブジェクトを解放しますが、そのときにそれらのクライアントは必要なオブジェクトを作成します。クライアント アプリケーションが、実際にはわずかな時間しかオブジェクトを使用していないのに、長期間オブジェクトを保持し続けて離さないことで、クライアントサーバー モデルとの拡張性が阻害されます。各クライアントにオブジェクトを割り当てると、そのような重要な、もしくは限られたリソースを長期間拘束し、結局リソースを使い果たすことになります。

クライアントからの呼び出しが行われている間のみクライアント向けのオブジェクトをサービスに割り当てるのが、より優れたアクティブ化モデルです。この方法だと、未処理のクライアントの数だけオブジェクトを作成するのではなく、同時に発生する呼び出しの数だけメモリ内にオブジェクトを作成し管理する必要があります。呼び出し間で、クライアントは、終端に実際のオブジェクトを持たないプロキシの参照を保持します。明らかな利点は、サービス インスタンスが長期間占有している、コストがかかるリソースの解放を行ってからクライアントがプロキシを解放することができる点です。さらに、クライアントが実際に必要とするまでリソースの取得は後回しにされます。2 番目の利点は、サービス インスタンスがすべての呼び出しについて、サービス インスタンスのリソースへの再割り当てあるいは接続を行うよう強制することにより、インスタンスの状態との整合性を強制するという作業が容易に行われるため、トランザクション リソースおよびトランザクション プログラミングの要求を十分に満たす点です。

パーコール サービスとしてサービスの種類を構成するには、InstanceContextMode プロパティを InstanceContextMode.PerCall に設定した ServiceBehavior 属性を適用します。

[ServiceContract]
interface IMyContract {...}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] 
class MyService : IMyContract {...}

InstanceContextMode.PerCall は、そのプロパティの既定値であるため、実際には属性を適用する必要はありません。したがって、次の 2 つの定義は同じ意味です。

class MyService : IMyContract {...}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] 
class MyService : IMyContract {...}

理論では、パーコール インスタンスのアクティブ化モードはいかなるサービス上でも使用できますが、実際には、パーコール アクティブ化モードをサポートするように、サービスおよびそのコントラクトをゼロから設計する必要があります。主な問題は、クライアントが毎回新しいインスタンスを取得していることに気付いていないことです。

パーコール サービスが状態感知 (ある呼び出しからの状態が将来の呼び出しでも持続すべきであることを指します) でなければならない場合、実装自体が先を見越して状態を管理し、クライアントにセッションが継続的であるという錯覚を与えなければなりません。パーコール サービスのインスタンスは、すべてのメソッド呼び出しの直前に作成され、各呼び出しの後に即時に破棄されます。したがって、各呼び出しの開始時に、オブジェクトはそのオブジェクト自体の状態をある記憶装置に保存されている値から初期化し、呼び出しの終了時にその状態を記憶装置に返さなくてはなりません。一般的には、そのような記憶装置はデータベースあるいはファイル システムのいずれかですが、静的変数などの揮発性記憶装置でもかまいません。ですが、すべてのオブジェクトの状態が現状のままで保存可能というわけではありません。たとえば、状態にデータベース接続が含まれている場合、オブジェクトは構築時あるいはすべての呼び出しの開始時に接続を再度取得して、呼び出しの終了時あるいは IDisposable.Dispose の実装内で接続を解放しなければなりません。

パーコール インスタンス モードを使用することには、オペレーション設計に対するある重要な推測が含まれます。すべてのオペレーションには、その状態の取得が必要なサービス インスタンスを特定するためのパラメータが含まれなければなりません。そのようなパラメータの例には、銀行口座サービスの口座番号、注文処理サービスの注文番号などがあります。図 3 は、パーコール サービス実装のためのテンプレートを示します。

図 3 パーコール サービスの実装

[DataContract]
class Param {...}

[ServiceContract]
interface IMyContract
{
    [OperationContract] 
    void MyMethod(Param objectIdentifier);
}

class MyPerCallService : IMyContract, IDisposable
{
    public void MyMethod(Param objectIdentifier)
    {
        GetState(objectIdentifier);
        DoWork();
        SaveState(objectIdentifier);
    }
    void GetState(Param objectIdentifier) {...}
    void DoWork() {...}
    void SaveState(Param objectIdentifier) {...}
    public void Dispose() {...}
}

MyMethod オペレーションは、次に示すようにインスタンスを特定する Param 型 (この例のために作り出された擬似型) のパラメータを受け入れます。

public void MyMethod(Param objectIdentifier);

そして、インスタンスは識別子を使用して状態を取得し、その状態をメソッド呼び出しの終了時に保存して戻します。すべてのクライアントに共通な状態のいかなる部分もコンストラクタで割り当てられ、Dispose で解放されます。

また、パーコール アクティブ化モードは、各メソッド呼び出しで行われる作業の総量が小さく、メソッドが戻ればバックグラウンドで完了させるアクティビティがこれ以上ないときに、最も良く動作します。この理由から、バックグラウンド スレッドを除去したり、非同期呼び出しをインスタンスに割り当てるべきではありません。それは、メソッドが戻るとオブジェクトは廃棄されるためです。

パーコール サービスは、明らかにパフォーマンス (各メソッド呼び出しのインスタンス状態を再構築することのオーバーヘッド) と拡張性 (状態と状態が拘束するリソースを保持し続けて離さないこと) とのトレードオフを提供します。豊富な拡張性に対してパフォーマンスについて妥協するタイミング、およびどの程度妥協するのかに関しては、厳格な規則はありません。システムをプロファイルしたり、そして究極的には、パーコール アクティブ化を使用するためにサービスをいくつか設計したり、パーコール アクティブ化を使用しないために再度設計し直したりする必要がある場合があります。

ページのトップへ


2. パーセッション サービス

Windows Communication Foundation は、クライアントと特定のサービス インスタンス間でプライベート セッションを管理することができます。クライアントが、セッション感知として構成されたサービスへの新しいプロキシを作成すると、クライアントは、同じサービスのすべての他のインスタンスに依存しない新しい専用サービス インスタンスを取得します。そのインスタンスは通常、クライアントが必要としなくなるまでサービスに残り続けます。各プライベート セッションは、プロキシと特定のサービス インスタンスとを一意にバインドさせます。クライアント セッションは、プロキシごとに 1 つのサービス インスタンスを持つことに注意してください。クライアントが同じあるいは異なるエンドポイントに別のプロキシを作成する場合、2 番目のプロキシは新しいインスタンスおよびセッションに関連付けられます。

サービス インスタンスはセッションを通してメモリに残り続けるため、サービス インスタンスは状態をメモリ内で管理します。そして、プログラミング モデルは典型的なクライアントサーバー モデルに非常に良く似ています。したがって、これも典型的なクライアント サーバー モデルと同じような拡張性およびトランザクションの問題に苦しみます。プライベート セッション向けに構成されたサービスは一般的に、そのような各専用サービス インスタンスに関連するコストのため、数十個 (あるいはおそらく数百個という) 未処理のクライアントをサポートできません。

セッションのサポートにはコントラクトおよび振る舞いの 2 つのファセットがあります。クライアント サイドの Windows Communication Foundation ランタイムは、セッションを使用すべきかどうかを知る必要があるため、コントラクト ファセットは、サービス境界を超えて必要とされます。ServiceContract 属性は、Boolean の Session プロパティを提供します。

[AttributeUsage(AttributeTargets.Interface|AttributeTargets.Class,
    Inherited=false)]
public sealed class ServiceContractAttribute : Attribute
{
    public bool Session {get;set;}
    ... // さらなるメンバ
}

Session は既定で False です。セッションをサポートするには、コントラクト レベルで Session を True に設定する必要があります。

[ServiceContract(Session = true)]
interface IMyContract {...}

構成を完了するには、セッションを通してサービス インスタンスをアクティブに保つよう Windows Communication Foundation に指示して、クライアント メッセージをそれに向けて送信する必要があります。このローカルの振る舞いファセットは、次に示すように、ServiceBehavior 属性の InstanceContextMode プロパティを InstanceContextMode.PerSession に設定することで取得されます。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] 
class MyService : IMyContract {...}

セッションは一般的に、クライアントがプロキシをクローズして、サービスに対してセッションが終了したことが通知されると終了します。サービスが IDisposable をサポートしている場合、Dispose メソッドが呼び出されます。図 4 は、プライベート セッションおよびそのクライアントを使用するよう構成されたサービスならびにコントラクトを示します。出力からお分かりのように、クライアントは専用インスタンスを取得しました。

図 4 パーセッション サービスおよびクライアント

サービスのコード

[ServiceContract(Session = true)]
interface IMyContract
{
    [OperationContract]
    void MyMethod();
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
class MyService : IMyContract,IDisposable
{
    int m_Counter = 0;

    MyService()
    {
        Trace.WriteLine("MyService.MyService()");
    }
    
    public void MyMethod()
    {
        m_Counter++;
        Trace.WriteLine("Counter = " + m_Counter);
    }

    public void Dispose()
    {
        Trace.WriteLine("MyService.Dispose()");
    }
}

クライアントのコード

MyContractProxy proxy = new MyContractProxy();
proxy.MyMethod();
proxy.MyMethod();
proxy.Close();

出力

MyService.MyService()
Counter = 1
Counter = 2
MyService.Dispose()

パーセッション インスタンス モードは、アプリケーション レベルでクライアントおよびサービス インスタンス間でセッションを提供します。このセッションは基底のチャネル セッションとしてのみ信頼できます。したがって、セッション感知のコントラクトを実装するサービスは、コントラクトを公開するすべてのエンドポイントが、信頼できるトランスポート セッションをサポートするバインディングを使用することを要求します。この制約は、サービスのロード時に検証され、一致しない場合は InvalidOperationException が発生します。信頼性をサポートするバインディングを使用して、プログラムあるいは管理上のいずれかで、クライアントおよびサービス両方でそれを明示的に有効にしていることを確認してください。この規則の例外は、名前付きパイプ バインディングです。このバインディングは、信頼できるトランスポートとして考えられ、信頼できるメッセージング プロトコルを必要としません。その上、いずれにしてもすべての呼び出しが同じマシン上に存在します。

ちょうど信頼できるトランスポート セッションが任意であるように、順序が決まっているメッセージ送付も任意ですが、順序が決まっている送信が無効になった場合でも Windows Communication Foundation はセッションを提供します。一般的に、セッション感知サービスとやりとりを行うクライアントは、すべてのメッセージが送信された順序で送付されることを期待します。このように、信頼できるトランスポート セッションが有効であり、したがって追加設定が必要ない場合に、順序が決まっている送付は既定で有効です。

一般的に、クライアントがプロキシをクローズするとセッションは終了します。ただし、クライアントが適切に終了できないか、通信に問題がある場合、各セッションもまたアイドル時間タイムアウトがあり、既定で 10 分に設定されています。そのためクライアントが 10 分間アクティブにならないと、クライアントがセッションを使用する予定であってもセッションは自動的に終了します。アイドル タイムアウトのためにセッションが終了すると、クライアントがそのプロキシを使用しようとしても、クライアントは CommunicationObjectFaultedException を受け取ります。

クライアントおよびサービスの両方が、バインディングに異なる値を設定することで、異なるタイムアウトを構成することができます。信頼できるトランスポートレベルのセッションをサポートするバインディングは、アイドル タイムアウトを構成するために使用される InactivityTimeout プロパティを持つ ReliableSession プロパティを提供します。たとえば、次に TCP バインディングのアイドル タイムアウトをプログラムで 25 分に構成することを要求するコードを示します。

NetTcpBinding tcpSessionBinding = new NetTcpBinding();

tcpSessionBinding.ReliableSession.Enabled = true;

tcpSessionBinding.ReliableSession.InactivityTimeout = 
    TimeSpan.FromMinutes(25);

これが、config ファイルを使用した同じようなコンフィグレーション設定です。

<netTcpBinding>
    <binding name="TCPSession">
        <reliableSession enabled="true" inactivityTimeout="00:25:00"/>
    </binding>
</netTcpBinding>

クライアントおよびサービス両方がタイムアウトを構成する場合、短いほうのタイムアウトが優勢です。

ページのトップへ


3. 共有可能なサービス

Windows Communication Foundation は、オブジェクト参照をサービス境界を越えて渡すことを許可しません。オブジェクトとは、テクノロジ独特の実体であり、オブジェクトの共有はサービス指向の性質、技術的に中立なインタラクションに反するものです。しかし、あるクライアントが自分のセッションの現在の状態を別のクライアントと共有したいと考えることが時々あります。解決法は、共有可能なサービスです。共有可能なサービスは、パーセッション サービスのように振る舞いますが、重要な性質が 1 つ追加されています。それは、インスタンスが固有の ID を持っていて、クライアントが共有可能なサービスとのセッションを確立すると、クライアントがそのインスタンスへの論理参照を別のクライアントへ渡すことができる、ということです。2 番目のクライアントは独立したセッションを確立しますが、同じインスタンスを共有します。また、それらのセッションのそれぞれが、別々の非アクティビティ タイムアウトを使用し、他のセッションとは独立して有効期限が切れます。

InstanceContextMode プロパティを InstanceContextMode.Sharable に設定することで、共有可能なサービスを構成します。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Sharable)] 
class MyService : IMyContract {...}

共有可能なサービスを利用する方法が 2 つあり、その両方は同じ技法が変化したものです。最初の方法は、プロキシを同じアプリケーション ドメインで複製するというもので、2 番目の方法は、エンドポイント アドレスを Windows Communication Foundation 越しに他のクライアントに渡すというものです。

インスタンス参照を渡すことは、Windows Communication Foundation に対する興味深い挑戦となります。最初のクライアントがインスタンスを作成し、参照を 2 番目のクライアントに渡して、2 番目のクライアントがその参照を使用して自身のプロキシを作成する機会を得る前に 1 番目のクライアントが自身のプロキシをクローズしたらどうなるでしょうか。この状況を取り扱うために、Windows Communication Foundation は、既定で 1 分の待機タイムアウトを利用します。Windows Communication Foundation は、同じインスタンスにつながれているセッションを追跡します。クライアントのいずれかがプロキシをクローズしても、サービス インスタンスは解放されません。最後のセッションが終了して待機タイムアウトが失効すると、インスタンスは解放されます。Windows Communication Foundation はまた、待機タイムアウトを構成することを許可します。サービス ホストの config ファイルで、behaviorConfiguration プロパティをサービス要素に追加して、振る舞いのセクションをポイントするようにします。そのセクションで、instanceContextIdleTimeout プロパティを希望の待機タイムアウトに設定します。

<services>
    <service type="MySharableService" 
     behaviorConfiguration="ShortLingerBehaviour">
    ...
    </service>
</services>
<behaviors>   
    <behavior name="ShortLingerBehaviour" 
     instanceContextIdleTimeout="00:05:00" />
</behaviors>

アプリケーションにとって待機タイムアウトでは不十分である場合、関係するクライアントは、いつであればそのクライアント間でプロキシをクローズしても問題がないかを調整する必要があります。

ページのトップへ


4. プロキシの複製

同じアプリケーション ドメイン内の 2 つのクライアントがサービス インスタンスを共有したい場合に、プロキシの複製は便利です。2 つのクライアントがプロキシの参照を共有する場合、クライアントは、どちらがプロキシのクローズを担当し、いつクローズするかを調整する必要があります。この調整の必要性によって、2 つのクライアントを不必要な度合いで結合することになります。解決法は、2 つのクライアントに別々のプロキシを使用させながらも同じインスタンスをポイントさせるというものです。2 つのプロキシは独立したセッションを持つため、各クライアントは、他に影響を与えることなく自身のプロキシをクローズすることができます。

プロキシを複製するには、通常通りプロキシをサービスに作成し、そしてエンドポイント アドレス参照を作成することで、サービス インスタンスを明示的に解決します。これにはサービスのアドレスと、既存のインスタンスに接続するために必要ないくつかの付加的なアドレス ヘッダー情報が含まれます。エンドポイント参照を取得するには、IClientChannel インターフェイスの ResolveInstance メソッドを使用します。

public interface IClientChannel : ...
{
    EndpointAddress ResolveInstance();
    ... // さらなるメンバ 
}

IClientChannel は、ClientBase<T> プロキシの基底クラスの InnerChannel プロパティによって取得します。

ResolveInstance を呼び出すには、ResolveInstance が共有向けに構成されていることを確認して、インスタンス ID を取得するために、サービスとのやり取りが必要です。エンドポイント アドレスを取得したら、それを使用して新しいプロキシを作成します。エンドポイント セクション名を config ファイルおよびアドレスから取得するプロキシ コンストラクタを使用することができますが、この場合は config ファイルにあるアドレスは無視されます。あるいは、バインディングおよびアドレス オブジェクトを取得するプロキシ コンストラクタを使用することができます。または、プロキシの Endpoint プロパティによって、エンドポイント アドレスを構築後に明示的に設定することもできます。プロキシを作成するためにどの方法を選択するかに関係なく、エンドポイント アドレスのトランスポートは、使用されるバインディングのトランスポートと一致することを確認してください。これで、別のセッション内ではあるが同じサービス インスタンスを共有する、2 つの別個のプロキシができました。図 5 は、この技法を示します。

図 5 プロキシの複製

MyContractProxy proxy1 = new MyContractProxy();
proxy1.MyMethod();
IClientChannel channel = proxy1.InnerChannel;

// 簡単にするために、最初のプロキシからバインディングを取得
Binding binding = proxy1.Endpoint.Binding;
EndpointAddress address = channel.ResolveInstance();

MyContractProxy proxy2 = new MyContractProxy(binding,address);
proxy2.MyMethod();

proxy1.Close();
proxy2.Close();

InstanceContextMode.Sharable 向けに構成されたサービスを使用することを除いて図 4 と同じ定義を使用すると、図 5図 4 と同じ出力をもたらします。

ページのトップへ


5. インスタンスの共有

サービスが共有可能として構成される場合、Windows Communication Foundation はサービス境界を越えてそのサービスのインスタンスを共有させます。これは、共有されるインスタンスへの参照を、あるクライアントから別のクライアントへ、Windows Communication Foundation を超えて渡すことを意味します。受け取り側は、EndpointAddress をパラメータとして取るオペレーションを持つコントラクトを公開する必要があります。このようなやり取りを、図 6 で説明します。この図では、クライアントには共有可能なサービス A のインスタンスを持つセッションがあります。

図 6 参照を渡す
図 6 参照を渡す

  1. クライアントは、サービス A からエンドポイント アドレスを取得します。
  2. そしてクライアントは、アドレスをサービス B のインスタンスへ Windows Communication Foundation 越しに渡します。
  3. サービス B は、エンドポイント アドレスを使用してプロキシを再構築し、サービス A のインスタンスを呼び出します。

インスタンス B はまた、エンドポイント アドレスを別の内部クライアントへ渡すか、あるいは別のサービスに渡すか、などを行います。

元々のクライアントおよびインスタンス A がセッションを持つ必要があり、インスタンス B (あるいはアドレスを使用するクライアント) およびインスタンス A がセッションを持つ必要がある場合、クライアントおよびインスタンス B はセッションを持つ必要がまったくないことは注目に値します。

ちょうど述べたように、受け取り側のエンドは、次の例に示されるように EndpointAddress をパラメータとして受け入れる、ある種のコントラクトを実装しなければなりません。

 

[ServiceContract]
interface ISomeContract
{
    [OperationContract]
    void PassReference(EndpointAddress10 serviceAddress);
}

EndpointAddress はシリアル化できません。ただし、EndpointAddress10 という WS-Addressing 1.0 の物理フォーマットに対応するものを使用することで、これを安全に回避することができます。

ISomeContract を実装するサービスが MyOtherService という名前で、次のように定義される時を想定します。

class MyOtherService : ISomeContract
{
   public void PassReference(EndpointAddress10 serviceAddress)
   {
      MyContractProxy proxy = new MyContractProxy();
      proxy.Endpoint.Address = serviceAddress.ToEndpointAddress();
      proxy.MyMethod();
      proxy.Close();
   }
}

InstanceContextMode.Sharable 向けに構成されたサービスを使用することを除いて図 4 と同じ定義を使用すると、次のコードは同じ出力をもたらします。

MyContractProxy proxy1 = new MyContractProxy();
proxy1.MyMethod();

IClientChannel channel = proxy1.InnerChannel;
EndpointAddress serviceAddress = channel.ResolveInstance();

SomeContractProxy proxy2 = new SomeContractProxy();
proxy2.PassReference(EndpointAddress10.FromEndpointAddress  
    (serviceAddress));

proxy1.Close();
proxy2.Close();

共有可能なサービス インスタンスは、おそらく利点よりも損害を与える扱いにくいインスタンス管理技法であることが分かります。ある特定の共有シナリオについてはすばらしい利点がありますが、アプリケーションにとって待機タイムアウトでは不適切な場合には、セッション管理および可能性のあるライフサイクル結合に関して複雑な問題が発生します。この技法を避けるようにして、サービス参照を渡したりサービス インスタンスの状態を共有したりすることが必要ではない、設計での解決法を探すようにしてください。

ページのトップへ


6. シングルトン サービス

シングルトン サービスは、究極の共有可能サービスです。サービスがシングルトンとして構成されると、すべてのクライアントは個々に、クライアントがサービスのどのエンドポイントに接続しているかにかかわらず、同じ単一の既知のインスタンスに接続します。シングルトン サービスは永遠に存続し、ホストが終了するときにのみ解放されます。シングルトンは、ホストが作成されるときのまさに 1 度だけ作成されます。

シングルトンを使用すると、クライアントはシングルトン インスタンスを持つセッションを管理するか、あるいはトランスポートレベル セッションをサポートするバインディングを使用する必要があります。クライアントが消費するコントラクトにセッションがある場合、プロキシをクローズすることで、クライアントはセッションの終了のみ行い、シングルトン インスタンスの終了は行いません。さらに、セッションの有効期限が切れることはありません。シングルトン サービスがセッションのないコントラクトをサポートする場合、これらのコントラクトはパーコールではなく、同じインスタンスに接続されます。シングルトン サービスでは、ResolveInstance を呼び出すことができません。理由は明らかです。まさにその性質からシングルトンは共有され、各クライアントはそのクライアント自体のプロキシをシングルトンに対して単に作成しなければならないためです。

シングルトン サービスは、InstanceContextMode プロパティを InstanceContextMode.Single に設定することで構成します。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
class MySingleton : ...
{...}

図 7 は、2 つのコントラクトを持つシングルトン サービスを説明します。1 つはセッションを必要とし、もう 1 つは必要としません。クライアント呼び出しから分かるように、2 つのエンドポイントでの呼び出しは、同じインスタンスにルーティングされ、プロキシのクローズによってシングルトンが終了しません。

図 7 シングルトン サービスおよびクライアント

サービスのコード

[ServiceContract(Session=true)]
interface IMyContract
{
    [OperationContract]
    void MyMethod();
}

[ServiceContract]
interface IMyOtherContract
{
    [OperationContract]
    void MyOtherMethod();
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
class MySingleton : IMyContract, IMyOtherContract, IDisposable
{
    int m_Counter = 0;
    public MySingleton()
    {
        Trace.WriteLine("MyService.MyService()");
    }
    public void MyMethod()
    {
        m_Counter++;
        Trace.WriteLine("Counter = " + m_Counter);
    }
    public void MyOtherMethod()
    {
        m_Counter++;
        Trace.WriteLine("Counter = " + m_Counter);
    }
    public void Dispose()
    {
        Trace.WriteLine("MyService.Dispose()");
    }
}

クライアントのコード

MyContractProxy proxy1 = new MyContractProxy();
proxy1.MyMethod();
proxy1.Close();

MyOtherContractProxy proxy2 = new MyOtherContractProxy();
proxy2.MyOtherMethod();
proxy2.Close();

出力

MyService.MyService()
Counter = 1
Counter = 2

既定のコンストラクタを使用してシングルトンを作成し、初期化したくないことがたまにあります。おそらく不十分なリソースを初期化すると、シングルトン管理に非常に時間がかかり、最初のクライアントを不利にしたくないかもしれません。おそらくこの状態を初期化することで、いくつかのカスタム手順あるいはクライアントが入手できない (あるいはクライアントを困らせるべきではない) 特定の知識が必要となります。このようなシナリオをサポートするために、Windows Communication Foundation は、あらかじめ通常の CLR インスタンス化を直接使用してシングルトン インスタンスを作成し、インスタンスを初期化し、そしてシングルトン サービスとしてインスタンスを考慮するホストを開くことを許可します。ServiceHost クラスは、オブジェクトを受け入れる専用コンストラクタを提供します。

public class ServiceHost : ServiceHostBase,...
{
    public ServiceHost(object singletonInstance,
        params Uri[] baseAddresses);
    public virtual object SingletonInstance { get; }
    ... // さらなるメンバ
}

オブジェクトはシングルトンとして構成されなければならないことに注意してください。たとえば、図 8 のコードを考えてみます。クラス MySingleton は、最初に初期化され、その後シングルトンとしてホストされます。最初の呼び出しを行うクライアントは、既に初期化されているインスタンスに接続されます。

図 8 シングルトンの初期化およびホスティング

サービスのコード

[ServiceContract]
interface IMyContract
{
    [OperationContract]
    void MyMethod();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
class MySingleton : IMyContract
{
    int m_Counter = 0;

    // m_Counter にアクセス
    public int Counter {get;set;}

    public void MyMethod()
    {
        m_Counter++;
        Trace.WriteLine("Counter = " + Counter);
    }
}

ホストのコード

MySingleton singleton = new MySingleton();
singleton.Counter = 42;
ServiceHost host = new ServiceHost(singleton);
host.Open();

クライアントのコード

MyContractProxy proxy = new MyContractProxy();
proxy.MyMethod();
proxy.Close();

出力

Counter = 43

このようにシングルトンを初期化およびホストすると、シングルトンに直接ホスト側でアクセスしたいとも考えるかもしれません。Windows Communication Foundation は、ServiceHost の SingletonInstance プロパティを使用することで、ダウンストリーム オブジェクトがそのシングルトンに直接さかのぼることを可能にします。シングルトンのオペレーション呼び出しから続く呼び出しチェーンに属するものは、オペレーション コンテキストの読み取り専用 Host プロパティを使用して、常にホストにアクセスできます。

public sealed class OperationContext : ...
{
    public ServiceHostBase Host {get;}
    ... // さらなるメンバ 
}

シングルトン参照を取得したら、次に示すようにシングルトンと直接やり取りすることができます。

ServiceHost host = OperationContext.Current.Host as ServiceHost;
Debug.Assert(host != null);
MySingleton singleton = host.SingletonInstance as MySingleton;
Debug.Assert(singleton != null);
singleton.Counter = 388;

シングルトン インスタンスがホストに提供されないと、SingletonInstance は Null を返します。

シングルトンを持つことにより、シングルトンには、複数のクライアント間で共有したいと考えるいくつかの価値のある状態があることが分かります。問題は、複数のクライアントがシングルトンに接続すると、それらすべてが複数のワーカー スレッド上で同時に処理を行う可能性があることです。シングルトンは、状態の破壊を防ぐために状態に同期的にアクセスしなければなりません。言い換えるとこれは、同時に 1 つのクライアントだけがシングルトンにアクセスできることを意味します。シングルトンはシステムが大きくなるにつれて使い物にならなくなるほど、反応性および可用性が低下する可能性があります。

一般的に、アプリケーション ドメイン内に実在するシングルトンとうまくマップする場合に、シングルトン オブジェクトを使用します。実在するシングルトンは、まさにその性質から 1 つであり一意です。実在するシングルトンの例は、すべてのサービスがそのアクティビティをログに記録すべきグローバルな業務日誌、単一通信ポート、および単一の機械モーターです。ビジネス ロジックで将来、他のモーターおよび 2 番目の通信ポートを追加するなど、そのようなサービスを複数許可する可能性が少しでもある場合は、シングルトンの使用を避けてください。理由は明らかです。すべてのクライアントが明示的に既知のインスタンスに接続することに依存していて、複数のサービス インスタンスが利用可能である場合、クライアントは突然、正しいインスタンスにバインドする何らかの方法が必要となるためです。これには、アプリケーションのプログラミング モデルに対して深刻な推測が含まれます。

ページのトップへ


7. オペレーションの明確化

セッション コントラクトを扱うときに、オペレーション呼び出しに対する暗黙の命令が存在する場合が時々あります。他のオペレーションを最後に呼び出されなければならないときに、オペレーションによっては、最初に呼び出すことができないものがあります。たとえば、顧客の注文を管理するために使用されるこのコントラクトを考えてみます。

[ServiceContract(Session = true)]
interface IOrderManager
{
    [OperationContract]
    void SetCustomerId(int customerId);
    [OperationContract]
    void AddItem(int itemId);
    [OperationContract]
    decimal GetTotal();
    [OperationContract]
    bool ProcessOrders();
}

コントラクトには、次の制約があります。クライアントはまず、追加されるアイテムに対する顧客 ID を提供しなければなりません。そして、合計が計算されます。命令の処理が完了すると、セッションが終了します。

Windows Communication Foundation によって、コントラクト設計者は、OperationContract 属性の IsInitiating および IsTerminating プロパティを使用して、セッションの開始あるいは終了が可能あるいは不可能なオペレーションとしてコントラクト オペレーションを指定することができます。

[AttributeUsage(AttributeTargets.Method)]
public sealed class OperationContractAttribute : Attribute
{
    public bool IsInitiating {get;set;}
    public bool IsTerminating {get;set;}
    ... // さらなるメンバ 
}

これらのプロパティを使用すると、セッションの境界を区別することになります。そのため、この技法をオペレーションの明確化と呼びます。セッション感知サービスおよびシングルトンの両方が、クライアント セッションの管理を行うためにオペレーションの明確化を使用するコントラクトを実装することができます。

それらのプロパティの既定値では、IsInitiating が True に設定され、IsTerminating が False に設定されます。したがって、次の 2 つの定義は次に示すように同じ意味です。

[ServiceContract(Session = true)]
interface IMyContract
{
    [OperationContract]
    void MyMethod()
}

[ServiceContract(Session = true)]
interface IMyContract
{
    [OperationContract(IsInitiating = true, IsTerminating = false)]
    void MyMethod()
}

既定で、オペレーションはセッション境界を区別しません。オペレーションはセッション内の最初、最後、あるいは他のオペレーションの中で呼び出されます。既定ではない値を使用すると、メソッドが最初に呼ばれないか、最後に呼ばれるか、あるいは両方かを命令して、やり取りの制約を強制することができます (図 9 を参照)。

図 9 オペレーションの明確化の特定

サービスのコード

[ServiceContract(Session = true)]
interface IOrderManager
{
    [OperationContract]
    void SetCustomerId(int customerId);

    [OperationContract(IsInitiating = false)]
    void AddItem(int itemId);

    [OperationContract(IsInitiating = false)]
    decimal GetTotal();

    [OperationContract(IsInitiating = false, IsTerminating = true)]
    bool ProcessOrders();
}

クライアントのコード

OrderManagerProxy proxy = new OrderManagerProxy();
proxy.SetCustomerId(123);
proxy.AddItem(4);
proxy.AddItem(5);
proxy.AddItem(6);
proxy.ProcessOrders();
proxy.Close();

IsInitiating が True (既定) に設定されると、これがクライアントに呼び出された最初のメソッドである場合、オペレーションは新しいセッションを開始しますが、他のオペレーションが最初に呼ばれる場合、継続しているセッションの一部分であることを意味します。IsInitiating が False に設定されると、このオペレーションはクライアントにより新しいセッションで最初のオペレーションとして呼ばれておらず、メソッドは単に継続しているセッションの一部分であることを意味します。

IsTerminating が False (既定) に設定されると、セッションは、オペレーションが戻った後も続行します。IsTerminating が True に設定されると、セッションはメソッドが戻ると終了し、クライアントは追加の呼び出しをプロキシで発行します。オペレーションはサービス インスタンスを解放せず、単に後続の呼び出しを拒否するだけであるため、クライアントはやはりプロキシを閉じなければならないことに注意してください。

ページのトップへ


8. インスタンスのアクティブ化解除

これまで記述してきたように、セッション感知サービス インスタンス管理技法では、クライアントをサービス インスタンスに接続します。しかし実際はもっと複雑です。各サービス インスタンスは、図 10 に示すようにコンテキストにホストされます。

図 10 コンテキストおよびインスタンス
図 10 コンテキストおよびインスタンス

実際にセッションは、クライアント メッセージをインスタンスではなく、それをホストするコンテキストに関連させます。セッションが開始すると、ホストは新しいコンテキストを作成します。セッションが終了すると、コンテキストは終了します。既定で、コンテキストのライフラインと、それがホストするインスタンスのライフラインは同じです。しかしながら最適化の目的で、Windows Communication Foundation はサービス設計者に、2 つのライフラインを分離させ、コンテキストから分離してインスタンスのアクティブ化解除を行うという選択肢を提供します。実際には、Windows Communication Foundation も、インスタンスを持たないコンテキストの状態を許可します。このインスタンス管理技法を、コンテキストのアクティブ化解除と呼びます。コンテキストのアクティブ化解除を制御する普通の方法とは、OperationBehavior 属性の ReleaseInstanceMode プロパティによる方法です。

public enum ReleaseInstanceMode
{
    None, BeforeCall, AfterCall, BeforeAndAfterCall
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class OperationBehaviorAttribute : Attribute,...
{
    public ReleaseInstanceMode ReleaseInstanceMode {get;set;}
    ... // さらなるメンバ
}

ReleaseInstanceMode のさまざまな値により、メソッド呼び出しに関してインスタンスのリリースを行う時期 (前、後、前後、あるいは行わない) を指定します。インスタンスを解放するときは、サービスが IDisposable をサポートする場合は Dispose メソッドが呼び出されます。

一般的に、サービス メソッドのすべてではなくいくつかのみに対して、あるいは異なるメソッドの異なる値だけでインスタンスのアクティブ化解除を適用します。もし均等に適用すると、パーコールのようなサービスとなるだけで、そのような方法で構成するのと同じことになります。インスタンス アクティブ化解除に依存して、ある呼び出しの順序を想定する場合は、オペレーションの明確化を使用して順序を強制しようとすることができます。

ReleaseInstanceMode プロパティの既定値は、ReleaseInstanceMode.None です。図 11 に示すように、ReleaseInstanceMode.None は、インスタンス ライフラインが呼び出しによる影響を受けていないことを意味します。

図 11 インスタンスのアクティブ化解除モードの比較
図 11 インスタンスのアクティブ化解除モードの比較

メソッドが ReleaseInstanceMode.BeforeCall で構成されている場合に、セッションにインスタンスが既に存在するときは、Windows Communication Foundation はそのアクティブ化解除を行い、新しいインスタンスをその場所に作成し、そして新しいインスタンスが呼び出しを行うようにします (図 11 に示すように)。クライアントがブロックすると、呼び出しが発生する前に受信スレッドが、インスタンスのアクティブ化解除のプロセスおよび呼び出し解放を処理します。これによりアクティブ化解除が、呼び出しと同時にではなく呼び出し前に、確かにアクティブ化されるようにします。ReleaseInstanceMode.BeforeCall の目的は、Open のようなメソッドを最適化することであり、貴重なリソースを取得しますが同時に以前に割り当てられたリソースをリリースします。セッション開始時にリソースを取得するのではなく、Open メソッドを待ってから、両方が以前に割り当てられたリソースをリリースして、新しいリソースを割り当てます。Open が呼び出されると、インスタンスの他のメソッドの呼び出しがいつでも開始できます。これらのメソッドは一般的に、ReleaseInstanceMode.None で構成されます。

メソッドが ReleaseInstanceMode.AfterCall で構成されると、Windows Communication Foundation は呼び出し後にインスタンスのアクティブ化解除を行います (図 11 を参照)。これは、セッションが終了するのを待たずに、インスタンスが保持する貴重なリソースをクリーンアップするメソッド (Close のような) を最適化することが目的です。ReleaseInstanceMode.AfterCall は一般的に、ReleaseInstanceMode.None で構成されているメソッドの後に呼び出されるメソッドに適用されます。

メソッドが ReleaseInstanceMode.BeforeAndAfterCall で構成されると、そのメソッドには、その名前が暗に示すように ReleaseInstanceMode.BeforeCall と ReleaseInstanceMode.AfterCall との複合効果があります。これは、図 11 に示されます。

ReleaseInstanceMode.BeforeAndAfterCall は、一見して不必要に思われますが、実際には他の値を補います。この目的は、ReleaseInstanceMode.BeforeCall/None でマークされるメソッドの後あるいは ReleaseInstanceMode.AfterCall/None でマークされるメソッドの前に呼び出されるメソッドに適用することです。リソースの割り当ておよびセキュリティの参照を最適化する必要があるときに、リソースを保持したまま、セッション感知サービスが状態感知の振る舞い (パーコール サービスのような) を利用したいという状況を考えてみます。もし ReleaseInstanceMode.BeforeCall が唯一利用可能な選択肢であるとすると、リソースがいまだにオブジェクトに割り当てられているが使用されていないときには、呼び出し後にある期間が経っているかもしれません。もし ReleaseInstanceMode.AfterCall が唯一利用可能な選択肢であるとすると、同じような状況が発生するかもしれません。なぜなら、リソースが無駄に消費されているときに、呼び出し前にある期間が経っているかもしれないためです。

インスタンスのアクティブ化解除にどのメソッドを使用するかをデザインタイムに決定せずに、メソッドが戻った後にインスタンスを非アクティブ化するための決定をランタイムに行うことができます。これを行うには、インスタンスのコンテキストで ReleaseServiceInstance メソッドを呼び出します。オペレーション コンテキストの InstanceContext プロパティにより、インスタンス コンテキストを取得します。

public sealed class InstanceContext : CommunicationObject,...
{
    public void ReleaseServiceInstance();
    ... // さらなるメンバ
}

public sealed class OperationContext : ...
{
    public InstanceContext InstanceContext {get;}
   ... // さらなるメンバ 
}

図 12 は、この技法を示します。

図 12 ReleaseServiceInstance の使用

[ServiceContract(Session = true)]
interface IMyContract
{
    [OperationContract]
    void MyMethod();
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)] 
class MyService : IMyContract,IDisposable
{
    public void MyMethod()
    {
        // 何か処理を行ってから
        OperationContext.Current.InstanceContext.
            ReleaseServiceInstance();
    }

    public void Dispose() {...}
}

ReleaseServiceInstance の呼び出しには、ReleaseInstanceMode.AfterCall の使用と同じような効果があります。ReleaseInstanceMode.BeforeCall が使われているメソッドを使用するときには、ReleaseInstanceMode.BeforeAndAfterCall 使用時と同じような効果があります。

インスタンスのアクティブ化解除は最適化の技法で、このような技法のすべてのように、一般的な状況では避けるべきです。パフォーマンスと拡張性の両目標を達成できなかった場合、そして注意深く調査しプロファイリングを行うことにより、これを使用すると状況が改善されることが疑う余地なく証明されているときのみ、インスタンスのアクティブ化を使用することを考慮します。拡張性とスループットが懸念事項である場合、パーコール インスタンス化モデルの簡素さを選択し、インスタンスのアクティブ化解除を行わないようにします。

ページのトップへ


9. スロットリング

スロットリングは、インスタンスを直接管理する技法ではなく、クライアント接続を抑制してサービス上の接続をロードします。スロットリングにより、割り当てられ使用される基底のリソースおよびサービスを最大限に消費し尽くさないようにします。

既定のスロットリング設定は、無制限です。時間がかかる場合に、構成したスロットリング設定を超過するときは、Windows Communication Foundation は自動的にキューに保留コーラーを置き、キューから順番に取り出して処理します。スロットリングはサービスの種類ごとに行われます。つまり、スロットリングはサービスのすべてのインスタンスおよびすべてのエンドポイントに影響を与えます。これは、スロットルとサービスが使用するすべてのチャネル ディスパッチャとの関連付けにより行われます。

Windows Communication Foundation により、図 13 に示されるようにサービス消費パラメータを制御することができます。パーセッション サービスでは、Max Instances は同時にアクティブなインスタンスの数と同時に発生しているセッションの数とを合計した数です。共有セッションサービスでは、Max Instances は同時にアクティブなインスタンスを単に合計した数です (各共有インスタンスが複数のセッションを持つことができるため)。パーコール サービスでは、インスタンス数は同時に発生している呼び出しの数と実際は同じです。したがって、パーコール サービスのインスタンスの最大数は、Max Instances および Max Concurrent Calls の最小数です。Max Instances は、シングルトン サービスを無視します。なぜなら、単一のインスタンスのみ持つことができるためです。

図 13 ServiceThrottle パラメータ

パラメータ 説明
Max Connections サービスに接続している未処理のクライアントの総数。
Max Concurrent Calls すべてのサービス インスタンスにわたって現在進行中の呼び出しの合計数。
Max Instances 同時にアクティブなコンテキストの合計数。インスタンスのコンテキストへのマップ方法は、インスタンス コンテキスト管理モードにより決定されます。

ページのトップへ


10. スロットリングの構成

スロットリングは通常 config ファイルで構成されます。これにより、長期にわたって、あるいは配置サイトを越えて、同じサービス コードをスロットルできます。ホストはまた、ランタイムでの決定に基づいてプログラムでスロットリングを構成することができます。

図 14 は、ホストの config ファイルでのスロットルの構成方法を示します。behaviorConfiguration タグを使用して、スロットルされた値を設定するカスタムの振る舞いをサービスに追加します。

図 14 管理側のスロットリング

<system.serviceModel>
    <services>
        <service type = "MyService" 
                 behaviorConfiguration = "ThrottledBehavior">
        ...
        </service>
    </services>
    <behaviors>
        <behavior name="ThrottledBehavior">
            <throttling 
               maxConcurrentCalls = "12" 
               maxConnections = "34" 
               maxInstances = "56" 
            />
        </behavior>
    </behaviors>
</system.serviceModel>

ホスト処理は、ランタイム パラメータに基づいてプログラムでサービスをスロットルできます。これを行うことができるのは、ホストが開かれる前のみです。ホストは、config ファイル内にあるスロットリングの振る舞いを、コンフィグレーションを削除して自身のコンフィグレーションを追加することでオーバーライドすることができますが、通常は config ファイル内にスロットリングの振る舞いが存在しないときのみプログラム上でスロットリングの振る舞いを提供すべきです。

ServiceHostBase クラスは、ServiceDescription 型の Description プロパティを提供します。

public class ServiceHostBase : ...
{
    public virtual ServiceDescription Description {get;}
    ... // さらなるメンバ 
}

その名前が暗に示すように、ServiceDescription はサービスのすべての特徴と振る舞いを記述したものです。ServiceDescription には、ジェネリック パラメータとして IServiceBehavior を持つ KeyedByTypeCollection<I> 型の Behaviors と呼ばれるプロパティが含まれています。

public class KeyedByTypeCollection<I> : KeyedCollection<Type,I>
{
    public T Find<T>();
    public T Remove<T>();
    ... // さらなるメンバ
}

public class ServiceDescription
{
    public KeyedByTypeCollection<IServiceBehavior> Behaviors {get;}
}

IServiceBehavior は、すべての振る舞いクラスおよび属性が実装するインターフェイスです。KeyedByTypeCollection<I> は、ジェネリック メソッド Find<T> を提供します。これは、コレクションに存在する場合は要求された振る舞いを返し、存在しなければ Null を返します。与えられた振る舞いの種類は、多くても 1 度しかコレクションに現れません。

図 15 は、スロットルされた振る舞いのプログラムでの設定方法を示します。最初にホスティング コードが、サービス スロットリングの振る舞いが config ファイルで提供されていないことを検証します。これを行うには、ServiceThrottlingBehavior を型パラメータとして使用して、KeyedByTypeCollection<I> の Find<T> メソッドを呼び出します。 ServiceThrottlingBehavior は、System.ServiceModel.Design 名前空間で定義されます。

図 15 プログラム上のスロットリング

ServiceHost host = new ServiceHost(typeof(MyService));

ServiceThrottlingBehavior throttle;
throttle = host.Description.Behaviors.Find<ServiceThrottlingBehavior>();

if(throttle == null)
{
    throttle = new ServiceThrottlingBehavior();
    throttle.MaxConcurrentCalls = 12;
    throttle.MaxConnections  = 34;
    throttle.MaxInstances   = 56;
    host.Description.Behaviors.Add(throttle);
}

host.Open();

public class ServiceThrottlingBehavior : IServiceBehavior
{
    public int MaxConcurrentCalls {get;set;}
    public int MaxConnections {get;set;}
    public int MaxInstances {get;set;}
    ... // さらなるメンバ 
}

返されたスロットルが Null である場合、ホスティング コードは新たに ServiceThrottlingBehavior を作成し、その値を設定し、それをサービス記述の振る舞いに追加します。

ページのトップへ


11. スロットルされた値の読み込み

スロットルされた値は、診断および分析目的でサービス開発者によってランタイムに読み込まれます。サービス インスタンスは、ディスパッチャからスロットルされたプロパティにランタイムにアクセスすることができます。まず最初に、オペレーション コンテキストからホストへの参照を取得します。ホスト ベース クラス ServiceHostBase は、読み取り専用の ChannelDispatchers プロパティという、厳密に型付けされた、ChannelDispatcherBase オブジェクトのコレクションを提供します。コレクション内の各アイテムは、ChannelDispatcher 型です。ChannelDispatcher は、構成されてスロットルされた値を含むプロパティ ServiceThrottle を提供します (図 16 を参照)。

図 16 スロットルされた値の読み込み

class MyService : ...
{
   public void MyMethod() // コントラクト オペレーション
   {
      ChannelDispatcher dispatcher = OperationContext.Current.Host.
          ChannelDispatchers[0] as ChannelDispatcher;
      
      ServiceThrottle serviceThrottle = dispatcher.ServiceThrottle;

      Trace.WriteLine("MaxConcurrentCalls = " + 
          serviceThrottle.MaxConcurrentCalls);
      Trace.WriteLine("MaxConnections = " +
          serviceThrottle.MaxConnections);
      Trace.WriteLine("MaxInstances = " + serviceThrottle.MaxInstances);
   }
}

サービスは、スロットルされた値を読み込むだけで、それらの値に影響を与えないことに注意してください。サービスがスロットルされた値を設定しようとすると、InvalidOperationException が発生します。

ページのトップへ


12. まとめ

拡張性、パフォーマンス、およびスループットの点では、すべてのアプリケーションが独特である一方、Windows Communication Foundation は、さまざまなアプリケーションにわたって適用可能な、基準となるインスタンス管理技法を提供します。したがって、幅広い種類のシナリオやプログラミング モデルが可能となります。Windows Communication Foundation のインスタンス管理を理解して、正しいアクティブ化モードを選択することは重要ですが、各モードの適用は驚くほど簡単に行うことができます。さらに、オペレーションの明確化およびインスタンスのアクティブ化解除により、直接モードを補うことができます。

ページのトップへ


Juval Lowy は、IDesign のソフトウェア設計者であり、Windows Communication Foundation のトレーニングおよび設計コンサルティングを提供しています。現在同氏は、Windows Communication Foundation を包括的に扱う書籍の執筆に取り組んでおり、シリコン バレーの Microsoft 地域ディレクタも兼任しています。同氏の連絡先は、www.idesign.net (英語) です。


この記事は、MSDN マガジン - 2006 年 6 月からの翻訳です。

QJ: 060602

ページのトップへ