[IT Pro 道場補足] NLB (affinity : none) におけるステートフルな処理の方法
(2008/09/01 留意事項を追記)
IT Pro 道場 (東京) 補足
奥主がデモでお見せしたように、NLB を構成し、しかも affinity = none とした場合でも、Web サーバ / アプリケーションサーバ (WCF サービス) に分離した構成で快適に稼動させることができます。(というか、すみません、そんな素敵なデモをお見せするはずでしたので、今回は下記説明のみとします。)
この場合のポイントは、要求のたびにマシンが切り替わる可能性があるという点です。(お伝えしたように、アルゴリズム上、クライアント台数が少ないと偏りますが、台数が増えると、きれいに振り分けをやってくれます。) また、Web サーバ (ASP.NET の画面側) はマシン A につながり、アプリケーションサーバ (バックの WCF サービス) はマシン B に
つながることもあるかもしれません。
ご説明したように、Web サーバ、アプリケーションサーバの構成で組んだ場合には、セッションやワークフローがメモリ中に常駐しているため、このままでは、正しく動作しなくなってしまうことでしょう。
そこで、このような場合にどうすべきか、という点について以下に記載します。(尚、基本的な考え方は、以前、http://blogs.msdn.com/tsmatsuz/archive/2007/10/16/workflowservicehost-net-framework-3-5.aspx に記載しました。ここでは、それを実践しています。)
■ ASP.NET の UI 側 (Web サーバ)
セッションをデータベースに永続化させることで解決します。
- まず、セッションを保管するデータベースを作成します。
開発環境から、以下のスクリプトを取得し、sysadmin 権限をもつユーザでログインして SQL Server に実行します (または開発環境の上から aspnet_regsql.exe を実行しても良いです)
%sysdrive%\Windows\Microsoft.NET\Framework\{該当バージョン}\InstallSqlState.sql
- そして、Web サーバ上の .config に以下のように記述します。(IIS 7 上から UI で設定をおこなうこともできます。)
<sessionState
mode="SQLServer"
sqlConnectionString="data source=dj-sql01;user id=Dojo;password=P@ssw0rd"
cookieless="true"
timeout="20"
/>
これで、最初にご説明した「セッション」が、毎回データベースに書き込まれ、使われる際もデータベースからロードされて利用されますので、マシンが切り替わっても問題ないはずです。
さて、これで動かしてみるとわかりますが、実は、NLB ではこのままでは動作しません。
ASP.NETでは、クッキーへの記録等々の際などに独自のアルゴリズムでエンコードや暗号化をおこなっているのですが、その際に使用されるキーはデフォルトではマシンごとに自動生成されるようになっています。ですので、NLB を使って複数マシン間で動かすには、さらに、以下のように、.config を記述し、マシンキーを揃えておくようにしましょう。(system.web のセクション内に記述します。尚、以下はサンプルです。キーは、固有の値を生成してください。)
<machineKey validationKey="7D41602FE8732A1397838A31BD40B3A2B2537683B7A8EC95D6C19364765B0F7740EF96B7D442C066B841FDD7BF5283806497EF454591FEAF8DC4CB10F6CC0F99"
decryptionKey="DC1BE38548657569FED10D5381A843009CFB91F24325A53C" validation="SHA1" />
ですから、例えば、マシンを 1 台とし、ワーカープロセスの最大数を増やすことでプロセス分散した場合には、このmachineKey の記述は必要ありません。また細かな話ですが、sessionState で cookieless を true にしているのは、マシンをまたがることで cookie のドメインが変わってしまい cookie が見れなくなることを防ぐためですが、NLB の場合には、奥主が見せていたように、仮想的に同じアドレスですから、もしかしたら cookieless は false でも動くかもしれません。
■ ワークフローサービスホスト側 (ワークフローサービスホストを使ったアプリケーションサーバ)
さらに、UI 処理のバックエンドとしてサービスとして稼動している WorkflowServiceHost 側でも、同様に、ワークフローがメモリ中にあるので、これをデータベースに永続化する仕組みを使用します。
- まず、上記セッションのときと同様、永続化するためのデータベースを構成しておく必要があります。
sysadmin 権限をもつユーザで create dabase をおこなってデータベースを作成し、開発環境に存在する以下のスクリプトを (以下の順番で) 実行します。
%sysdrive%\Windows\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\JA\SqlPersistenceService_Schema.sql
%sysdrive%\Windows\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\JA\SqlPersistenceService_Logic.sql
- そして、.config に、以下のように追加します。(system.ServiceModel の中です)
<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=dj-sql01;Initial Catalog=PersistData;User ID=Dojo;Password=P@ssw0rd;Pooling=False"
LoadIntervalSeconds="1" UnLoadOnIdle= "true" />
</services>
</workflowRuntime>
以上で完了です。
稼動中にサーバを 1 台落としたり、あるいはまた稼動したままで、サーバを起動しなおし、今度は別のサーバを立ちあげたりと、いろいろ遊んでみてください。(注文情報なども維持されたまま、継続して動作させることができます。)
ただし、ワークフローサービスを使用した実装では、シナリオや求められるスケーラビリティによってカスタムの永続化サービスの実装などが必要になるので留意してください。スケーラビリティ全体を踏まえた考察については、こちら に内容を投稿しました。