Azureはじめました

Windows Azureで業務システムを組んでみる日記

IIS オート スタート設定による ASP.NET Web アプリケーションの初回実行時のパフォーマンス向上の罠

IIS オート スタート設定による ASP.NET Web アプリケーションの初回実行時のパフォーマンス向上 - THE TRUTH IS OUT THERE - Site Home - MSDN Blogsをやってて罠に嵌りまくった腹いせエントリ。

すいません言い過ぎました。

Windows Azure クラウドサービスで IIS/ASP.NET オートスタートを使う

続いて、この ASP.NET アプリケーションのオートスタートの設定を Windows Azure クラウド サービスを使った Web アプリケーションで有効にする方法をご紹介します。

クラウド サービスでも Web ロールにリモートデスクトップ (RDP) 接続して、上記と同じ手順で設定は可能です。しかしながら、スケールアップなどの際に、増やしたインスタンス一つ一つに RDP 接続して設定を行うのは現実的ではありません。オートスケールなどでインスタンスが自動で増減する場合はなおさらです。このような場合に、Windows Azure クラウドサービスでは、スタートアップタスクと、ロールの初期化時のイベントハンドラを利用することで、上記のオートスタートの設定を行うことができます。

IIS オート スタート設定による ASP.NET Web アプリケーションの初回実行時のパフォーマンス向上 - THE TRUTH IS OUT THERE - Site Home - MSDN Blogs

1. スタートアップタスクで Application Initialization モジュールを有効化する

まず、ASP.NET Web アプリケーションのプロジェクトに Startup.cmd ファイルを追加して、プロパティで [出力ディレクトリにコピー] を [常にコピーする] に設定します。

このStartup.cmdはUTF-8じゃなくてANSIじゃないとダメ。
VisualStudioでやってるとこれ忘れてクソハマる。超注意。

そして、Windows Azure クラウドサービス プロジェクトにある ServiceDefinition.csdef ファイルを開いて、下記の記述(太字部分)を追加します。

<ServiceDefinition ...>
  <WebRole name="WebAppPreload" vmsize="Small">

    <Runtime executionContext="elevated"></Runtime>

    <Startup>
 <Task commandLine="Startup.cmd" executionContext="elevated" taskType="simple" />
 </Startup>

Runtime要素はWebRoleの最初に無いとAzureEmulatorでビシバシ落ちた。

IIS 8.0 Application Initialization module in a Windows Azure Web Role

こっちの記事のサンプルを参考に
WindowsAzure-IISApplicationInitializationModule/ServiceDefinition.csdef at master · sandrinodimattia/WindowsAzure-IISApplicationInitializationModule

この順番にしてる。
何でダメなのかまでは調べてない。

2. アプリケーション プール開始モードと Web サイトのプリロード機能の有効化

続いて、applicationHost.config へ行うアプリケーションプールの開始モードと Web サイトのプリロードの有効化ですが、下記のようなクラスを Web アプリケーションプロジェクトに追加し、Microsoft.WindowsAzure.ServiceRuntime.RoleEntryPoint クラスの Run メソッドをオーバーライドして、その中でプログラムコードを通して設定します。

using Microsoft.Web.Administration;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace WebApplication1
{
    public class WebRole : RoleEntryPoint
    {
        public override void Run()
        {
            using (var serverManager = new ServerManager())
            {
                var mainSite = serverManager.Sites[RoleEnvironment.CurrentRoleInstance.Id + "_Web"];
                var mainApplication = mainSite.Applications["/"];
                mainApplication["preloadEnabled"] = true;

                var mainApplicationPool = serverManager.ApplicationPools[mainApplication.ApplicationPoolName];
                mainApplicationPool["startMode"] = "AlwaysRunning";

                serverManager.CommitChanges();
            }

            base.Run();
        }

        public override bool OnStart()
        {
            return base.OnStart();
        }
    }
}

これEmulator Express環境下でがっつり落ちた。
問題はここ

  var mainSite = serverManager.Sites[RoleEnvironment.CurrentRoleInstance.Id + "_Web"];
  var mainApplication = mainSite.Applications["/"];

どうやらserverManagerが正しくEmulatorExpressのインスタンスを指していないようで、Sitesには"Default Web Site"だけが登録されてた。おそらくIISそのもののデータを引いてるくさい。
その為にmainSite.Applications["/"]がぬるぽで死ぬ。

なのでローカルではここを迂回するようにした。

  var mainSite = serverManager.Sites[RoleEnvironment.CurrentRoleInstance.Id + "_Web"];
  if (mainSite!=null){
    var mainApplication = mainSite.Applications["/"];

    :
  }

というログ。