.NET Framework 3.5 の DurableService (WorkflowServiceHost 含む) をクラスタで構成する設計パターン


2008/06/19 追記: 本内容は、以下に Video 化しました
http://www.microsoft.com/japan/technet/community/events/dojo/online/self/6.mspx 

環境:
Visual Studio 2008 Beta 2

こんにちは。

今日は、.NET Framework 3.5 からの新機能である DurableService による WCF サービスや WorkflowServiceHost の WCF / WF Integration などの「ステート」が重要な意味を持つシナリオで、ロードバランス (負荷分散) 型などのクラスタ構成のケースを連想し、以下に、その実装のポイントを記載してみます。(以下は、実際に確認済のため動作します。)

前々回のブログで記載しましたが、今後、Dev - IT pro 連携型の実アプリ構築を主題とした展開などを考えています。その初発が IT Pro 道場で Developer の方向けにはしばらくご無沙汰になってしまいますので、今日は、Developer の方向けにその内容の一部を記載します。

  • ポイント1:永続化サービスの利用
    まず、WorkflowServiceHost の例で考えてみましょう。ワークフローの状態 (ステート) を分散されたサーバ間でレプリケートする必要性から、インスタンスは使用後に非活性化 (Unload) をおこない、次の使用にさいしてメモリ上に活性化 (Load) をおこなう必要があるでしょう。つまり、永続化サービスをうまく使う必要があります (残念ながら、メモリ間で差分レプリケーションをおこなう仕組みなどはありません)。
    では、「ワークフローの作成とランタイムサービスの利用」 でもご紹介しているように、WorkflowServiceHost などの Durable なサービスに対して PersistenceService を AddService することで実現しようと思うかもしれません。しかし、みて頂くとわかりますが、新しい WorkflowServiceHost には AddService メソッドはありません。
    WorkflowServiceHost では、実は、サービスの追加を WCF らしく構成ファイル(App.config)に記述することで実現できます。(厳密には、Runtime を取得して設定する方法もありますが、今回は省略します。) 永続化をおこなう場合には、.config に以下のように記述します。

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <services>
          <service name="OrderApplicationService.OrderWorkflow" behaviorConfiguration="ServiceBehavior" >
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8888/ServiceHost/OrderWorkflow.svc" />
              </baseAddresses>
            </host>
            <endpoint address=""
                      binding="wsHttpContextBinding"
                      contract="OrderApplicationService.IOrderWorkflow" />
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="ServiceBehavior"  >
              <serviceMetadata httpGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="true" />
              <serviceCredentials>
                <windowsAuthentication
                    allowAnonymousLogons="false"
                    includeWindowsGroups="true" />
              </serviceCredentials>
              <workflowRuntime name="WorkflowServiceHostRuntime" validateOnCreate="true" enablePerformanceCounters="true">
                <services>
                  <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                       connectionString="Data Source=kkdeveva01sqlexpress;Initial Catalog=PersistDB;User ID=SqlDev;Password=SqlDev0;Pooling=False"
                       LoadIntervalSeconds="1" UnLoadOnIdle= "true" />
                </services>
              </workflowRuntime>

            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    </configuration>

    同様の方法で、前回のブログで記載した WAS を使った場合でも web.config に記述して動かすことができます。また余談ですが、データ交換サービス (ExternalDataExchangeService, EDS) など他のサービスを使用する場合も同様に記述できます。(ただし、永続化に失敗する場合には、クライアント側には「ID ... のインスタンスが永続化ストアに見つかりませんでした」などの簡易なエラーメッセージしか届きませんので、サーバ側で Trace を出力するか、セルフホストをおこなってコンソール上で確認するなどしてエラーの詳細をトラップしてください。)

    さて、WorkflowServiceHost を使わない Durable サービスの場合 (つまり、DurableService メソッドを指定した WCF サービスの場合) には、WF の永続化サービスが使えず、今までのように永続化された Session などに持続すべき情報を保持して実現すべきなのでしょうか (こうなると、サーバ-クライアント間の対応関係も独自に実装しないといけなくなります)。
    実は、.NET Framework 3.5 では、DurableService の登場にあわせ、PersistenceProvider と呼ばれる専用の永続化エンジンを WCF に組み込むことができるようになっています。例により、1 から構築したくない人のために SQL Server に永続化する SqlPersistenceProvider というものがデフォルトで付属しています。(無論、sql スクリプトも提供されています。)

  • ポイント2:永続化サービスにおける Unload のタイミング
    さて、非活性化 (この書き方は Java っぽいのでやめましょう。Unload です) と Load (活性化) の基本的な仕組みはできましたが、今度はそのタイミングが問題です。せっかくこの仕組みを作っても、呼び出しで確実に Unload されないと 2 回目からの呼び出しは失敗します。
    WorkflowServiceHost は非常によく出来ています。ひとたび永続化サービスを設定すれば、明示的に Unload をしなくとも、アプリケーションプールや Web Site (または IIS) の再起動などランタイムの停止のタイミングで自動で Unload されます (つまり、これらインフラ系の終了イベントを拾って正しく WF Runtime を停止しています)。しかし、この仕組みは、単一のアプリケーションサーバ (WorkflowServiceHost が画面遷移の制御など Web サーバ側で動いているときは「Web サーバ」ですが) で、かつ InProc のセッションを使っているときには問題ありませんが、ラウンドロビンや重みによる設定など、要求先のノードが呼び出しのたびに切り替わるようなシナリオでは不都合です。
    このような状況に配慮し、.NET Framework ではさまざまな対応方法がとれるように設計されています。
    たとえば 1 つの方法は、UnloadOnIdel を true に設定し、Listen など使い、1 回の呼び出しに対して必ず 1 つの ReceiveActivity を実行したあとで Idle させることで、呼び出しに対して確実にインスタンスを Unload させることもできるでしょう。このシナリオの場合、各々の処理を ReceiveActivity の中のアクティビティ (ReceiveActivity はコンポジットアクティビティのため、その中に複数のアクティビティを持てます) とするか、あるいは別々の ReceiveActivity として呼び出すようにするかという設計の相違は重要になります。
    あるいは別の手段として、意味的に 1 回の呼び出しとなる箇所については、Unload や TryUnload を明示的に呼び出すという方法もあるでしょう。
    毎回処理が永続化されることはパフォーマンスを低下させる原因とはなりますが、それは従来のセッションの永続化同様、よほど永続化データが巨大なケース、よほど連続するケースなどを除いては、そのこと自体がアプリケーション全体のボトルネックのポイントになることは稀であると考えて良いでしょう。

  • ポイント3:クライアント側のステートの維持
    サーバアプリケーションを Web サーバ (UIC, UPC などの処理側) とアプリケーションサーバ(ビジネスロジックの処理側)に分散して、WorkflowServiceHost がアプリケーションサーバ側で動作するケースを考えてみてください。今度はアプリケーションサーバ (サービス) を利用する クライアント に相当する Web サーバ側も、毎回スレッドが消失することになります (どのようにステートを維持すれば良いのでしょう?)。上記までのシナリオだと、サーバ 側は準備万端ですが、このケース (クライアント側の管理) には対応できていません。
    従来、「Web サービス」をアプリケーションサーバ側に使用したWebサーバ側の管理手法では、Web サーバ側 (UI 処理側のサーバ) のスレッドで CookieContainer を作成し、これを Session に保持するという手法がありました。実は、.NET Framework 3.5 でもセッションを管理するオブジェクトをそのまま使用して、これと似たことが可能になっています。
    WCF と WF の連携」の最後で実施している Context を保持するデモ、及び添付されているソースコードを参照してみてください。デモではむずかしい説明を省略していますが、要は、WCF では、Context と呼ばれる抽象的な概念を保持していて、これをシリアライズすることができます (「抽象的」と記述している理由は、例えば wsHttpContextBinding の場合にはセッションなどとして実装され、netTcpContextBinding の場合にはインメモリの専用の領域に保持されるといった具合に、チャネルの実装に依存するためです)。
    このコードと同様の手法を使って、ASP.NET 上のクライアントから、取得したコンテキストをセッション (HttpContext.Current.Session) に保持して使用することが可能です。

今回は、DurableService という極端にステートフルなケースを想定しましたが、WCF のセッション (SessionMode=Required または Allowed, InstanceContextMode=InstanceContextMode.PerSession) を使用した一般のステートフルなサービス(生存状態が変化するステートではなく、生存していることが前提のステート) については、単に ASP.NET Compatibility Model と呼ばれる従来の HTTP の Session のテクノロジーを使う実現例もあります。

 

Comments (3)

  1. CodeZine の VS 2008 連載 第四回で、 構成ファイルを使用しないで利用する方法 (リンクは CodeZine 該当部分)について書かせてもらいました。 「この構成ファイルを使用しないで利用する。」という部分をちょっと深くつっこんだ解説をしてみたいと思います。気になる方は

  2. (2008/09/01 留意事項を追記) IT Pro 道場 (東京) 補足 IIS 7 を使ったモジュール統合 大量データを GridView で処理する NLB (affinity : none)

Skip to main content