web-dev-qa-db-ja.com

HttpModule Initメソッドが数回呼び出されます-なぜですか?

私はhttpモジュールを作成していましたが、デバッグ中に、最初は(少なくとも)奇妙な動作のように見える何かに気づきました。

Httpmoduleのinitメソッドにブレークポイントを設定すると、デバッグのためにWebサイトを起動しただけで、1回のリクエストを行っただけなのにhttp module initメソッドが数回呼び出されていることがわかります(1回だけヒットすることもあります)。 、他の場合は10回も)。

HttpApplicationの複数のインスタンスが実行され、httpモジュールごとに作成されることを期待する必要があることはわかっていますが、単一のページを要求すると、単一のhttpアプリケーションオブジェクトによって処理されるため、関連付けられたイベントは1回だけ発生します。それでも、リクエストごとにイベントを数回発生させます。これは、そのhttpApplication内に数回追加されている必要があることを除いて、意味がありません。つまり、新しいhttpアプリケーションではなく、毎回呼び出される同じhttpmoduleinitメソッドです。ブレークポイントに到達するたびに作成されます(下部のコード例などを参照してください)。

ここで何がうまくいかない可能性がありますか?デバッグしていて、httpモジュールにブレークポイントを設定しているからですか?

デバッグのためにWebサイトを起動し、httpmoduleのブレークポイントをすばやくステップオーバーすると、initメソッドが1回だけヒットし、イベントハンドラーにも同じことが当てはまるようです。代わりに、ブレークポイントで数秒間ハングさせた場合、initメソッドが数回呼び出されます(ブレークポイントをステップオーバーするまでの待機時間によって異なるようです)。これは、httpmoduleが初期化され、httpアプリケーションがリクエストを処理できることを確認するための組み込み機能である可能性がありますが、壊滅的な結果をもたらす可能性があるもののようにも思われます。

これは論理的に思えるかもしれません。リクエストを終了しようとしている可能性があり、ブレークポイントを設定したため、問題が発生したと考えて、initメソッドを再度呼び出そうとしますか?すっごくリクエストを処理できますか?

しかし、これは何が起こっているのか、そしてすべてがうまくいっていますか(私はただ推測しています)、それともそれは本当の問題ですか?

私が特に懸念しているのは、何かが「本番/ライブ」サーバーで数秒間ハングする場合、initを介して多くのイベントハンドラーが追加されるため、ページへの各リクエストが突然イベントハンドラーを数回起動することです。

この動作により、サイトがすぐにダウンする可能性があります。

Formsauthenticationのhttpmodulesやrolemanagermoduleなどに使用される「元の」.netコードを見てきましたが、私のコードはこれらのモジュールが使用するものと何ら変わりはありません。

私のコードは次のようになります。

    public void Init(HttpApplication app)
    {
        if (CommunityAuthenticationIntegration.IsEnabled)
        {
            FormsAuthenticationModule formsAuthModule = (FormsAuthenticationModule) app.Modules["FormsAuthentication"];         

            formsAuthModule.Authenticate += new FormsAuthenticationEventHandler(this.OnAuthenticate);
        }
    }

これは、.NETFrameworkのRo​​leManagerModuleでどのように行われるかの例です。

    public void Init(HttpApplication app)
    {
        if (Roles.Enabled)
        {
            app.PostAuthenticateRequest += new EventHandler(this.OnEnter);
            app.EndRequest += new EventHandler(this.OnLeave);
        }
    }

誰かが何が起こっているのか知っていますか?

(私はただ誰かがこれが起こっている理由を教えてくれて、すべてが完全にうまくいくことを私に保証してくれることを願っています):)


更新:

私は問題を絞り込もうとしましたが、これまでのところ、呼び出されているInitメソッドは常にhttpモジュールの新しいオブジェクト上にあることがわかりました(以前考えていたのとは逆です)。

最初のリクエスト(サイトの起動時)では、作成されているすべてのHttpApplicationオブジェクトとそのモジュールがすべて最初のリクエストを処理しようとしているため、追加されているイベントハンドラーにヒットしているようです。なぜこれが起こっているのか本当に理解できません。

別のページをリクエストすると、作成されたすべてのHttpApplication(およびそれらのモジュール)が再度リクエストを処理しようとし、イベントハンドラーに複数回ヒットします。

しかし、最初のページ(または別のページ)に戻ると、1つのHttpApplicationのみが要求の処理を開始し、すべてが期待どおりに行われるようです-ブレークポイントでハングさせない限り。

ブレークポイントでハングさせると、新しいHttpApplicationのオブジェクトの作成が開始され、リクエストを処理/処理するためのHttpApplications(1つ以上)の追加が開始されます(現在ブレークポイントで停止しているHttpApplicationによって処理されています)。 。

負荷やエラーの分散と処理を支援するインテリジェントな「舞台裏」の方法である可能性があると思います。しかし、私には手がかりがありません。私はそこにいる人がそれが完全に素晴らしいこととそれがどうあるべきかを私に保証できることを願っていますか?

31
MartinF
  1. HttpContext.Current.Requestを調べて、モジュールのinitが起動された要求を確認します。ブラウザが複数のリクエストを送信している可能性があります。

  2. IISに接続している場合は、IISログをチェックして、ブレークポイントに滞在している間に要求が受信されたかどうかを確認してください。

8
Ramesh

Init()メソッドが複数回呼び出されるのは正常です。アプリケーションが起動すると、ASP.NETワーカープロセスは必要と思われる数のHttpApplicationオブジェクトをインスタンス化し、それらをプールします(たとえば、データベース接続プールと同様に、新しい要求のためにそれらを再利用します)。

これで、HttpApplicationオブジェクトごとに、登録されている各IHttpModuleのコピーが1つインスタンス化され、Initメソッドが何度も呼び出されます。したがって、5つのHttpApplicationオブジェクトが作成されると、IHttpModuleの5つのコピーが作成され、Initメソッドが5回呼び出されます。意味がありますか?

では、なぜ5つのHttpApplicationsオブジェクトをインスタンス化するのでしょうか。 ASPXページには、ブラウザがダウンロードしようとする他のリソース、css、javascript、WebResource.aspx、おそらくどこかにあるiframeへのリンクがあるかもしれません。または、ASP.NETワーカープロセスが複数のHttpApplicationオブジェクトを開始する気分になっている可能性があります。これは、実際にはIIS(またはVSビルトインWebサーバー)。

一度だけ実行されることが保証されているコードが必要な場合(およびGlobal.asaxでApplication_StartUpイベントを使用したくない場合)、IHttpModuleで次のことを試すことができます。

private static bool HasAppStarted = false;
private readonly static object _syncObject = new object();

public void Init(HttpApplication context)
{
    if (!HasAppStarted)
    {
        lock (_syncObject)
        {
            if (!HasAppStarted)
            {
                // Run application StartUp code here

                HasAppStarted = true;
            }
        }
    }
}

私は似たようなことをしました、そしてそれはうまくいくようです、しかし私が何かを逃した場合に備えて私の仕事の批評を歓迎します。

42
Sunday Ironfoot
2
Greg Ogle

上記の例では、すべてのリクエストに対してIHttpModuleをロックしてから、アプリケーション全体をフリーズします。 IHttpModuleがリクエストを数回呼び出す場合は、HttpApplicationメソッドCompleteRequestを呼び出し、次のようにHttpApplicationのインスタンスを削除するために、EndRequestイベントでIHttpModuleのHttpApplicationインスタンスを破棄する必要があります。

public class TestModule :IHttpModule
    {
        #region IHttpModule Members

        public void Dispose()
        {

        }

        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(context_BeginRequest);
            context.EndRequest += new EventHandler(context_EndRequest);
        }

        void context_EndRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            app.CompleteRequest();
            app.Dispose();
        }

        void context_BeginRequest(object sender, EventArgs e)
        {
            //your code here
        }

        #endregion
    }

ポストバックで再リクエストせずに毎回IHttpModuleリクエストが必要な場合は、上記のコードを使用してください。

1
kinki