HttpModule Init方法被多次调用 - 为什么?

时间:2022-05-28 19:50:21

I was creating a http module and while debugging I noticed something which at first (at least) seemed like weird behaviour.

我正在创建一个http模块,在调试时我注意到一些东西,起初(至少)看起来像是奇怪的行为。

When i set a breakpoint in the init method of the httpmodule i can see that the http module init method is being called several times even though i have only started up the website for debugging and made one single request (sometimes it is hit only 1 time, other times as many as 10 times).

当我在httpmodule的init方法中设置断点时,我可以看到http模块初始化方法被多次调用,即使我只是启动了网站进行调试并发出了一个单一的请求(有时它只被命中一次) ,其他时间多达10次)。

I know that I should expect several instances of the HttpApplication to be running and for each the http modules will be created, but when i request a single page it should be handled by a single http application object and therefore only fire the events associated once, but still it fires the events several times for each request which makes no sense - other than it must have been added several times within that httpApplication - which means it is the same httpmodule init method which is being called every time and not a new http application being created each time it hits my break point (see my code example at the bottom etc.).

我知道我应该期望HttpApplication的几个实例正在运行,并且每个http模块都将被创建,但是当我请求单个页面时,它应该由单个http应用程序对象处理,因此只触发关联的事件一次,但是它仍然会为每个请求多次触发事件,这是没有意义的 - 除了它必须在httpApplication中多次添加 - 这意味着它是相同的httpmodule init方法,每次调用而不是新的http应用程序每次碰到我的断点时都会被创建(请参阅底部的代码示例等)。

What could be going wrong here ? is it because i am debugging and set a breakpoint in the http module?

这可能会出错?是因为我正在调试并在http模块中设置断点?

It have noticed that it seems that if i startup the website for debugging and quickly step over the breakpoint in the httpmodule it will only hit the init method once and the same goes for the eventhandler. If I instead let it hang at the breakpoint for a few seconds the init method is being called several times (seems like it depends on how long time i wait before stepping over the breakpoint). Maybe this could be some build in feature to make sure that the httpmodule is initialised and the http application can serve requests , but it also seems like something that could have catastrophic consequences.

它已经注意到,如果我启动网站进行调试并快速跳过httpmodule中的断点,它只会触发一次init方法,而事件处理程序也是如此。如果我让它在断点处挂起几秒钟,则会多次调用init方法(看起来这取决于我在跨越断点之前等待多长时间)。也许这可能是一些内置功能,以确保httpmodule初始化,http应用程序可以提供请求,但它似乎也可能产生灾难性后果。

This could seem logical, as it might be trying to finish the request and since i have set the break point it thinks something have gone wrong and try to call the init method again ? soo it can handle the request ?

这似乎是合乎逻辑的,因为它可能正在尝试完成请求,因为我设置了断点,它认为出了问题,并尝试再次调用init方法?它可以处理请求吗?

But is this what is happening and is everything fine (i am just guessing), or is it a real problem ?

但这是发生了什么,一切都很好(我只是在猜测),还是一个真正的问题?

What i am specially concerned about is that if something makes it hang on the "production/live" server for a few seconds a lot of event handlers are added through the init and therefore each request to the page suddenly fires the eventhandler several times.

我特别关注的是,如果有什么东西让它挂在“生产/实时”服务器上几秒钟,那么很多事件处理程序都会通过init添加,因此每次对页面的请求都会突然激活几次事件处理程序。

This behaviour could quickly bring any site down.

此行为可能会迅速导致任何网站崩溃。

I have looked at the "original" .net code used for the httpmodules for formsauthentication and the rolemanagermodule etc but my code isnt any different that those modules uses.

我查看了用于formauthentication和rolemanagermodule等的httpmodules的“原始”.net代码,但我的代码与这些模块使用的代码不同。

My code looks like this.

我的代码看起来像这样。

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

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

here is an example how it is done in the RoleManagerModule from the .NET framework

这是一个如何在.NET框架的RoleManagerModule中完成它的示例

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

Do anyone know what is going on?

有谁知道发生了什么事?

(i just hope someone out there can tell me why this is happening and assure me that everything is perfectly fine) :)

(我只希望有人可以告诉我为什么会这样,并向我保证一切都很好):)


UPDATE:

更新:

I have tried to narrow down the problem and so far i have found that the Init method being called is always on a new object of my http module (contary to what i thought before).

我试图缩小问题的范围,到目前为止,我发现被调用的Init方法总是在我的http模块的一个新对象上(与我之前的想法一致)。

I seems that for the first request (when starting up the site) all of the HttpApplication objects being created and its modules are all trying to serve the first request and therefore all hit the eventhandler that is being added. I cant really figure out why this is happening.

我似乎对于第一个请求(启动站点时)所有正在创建的HttpApplication对象及其模块都试图为第一个请求提供服务,因此所有请求都会触发正在添加的事件处理程序。我无法弄清楚为什么会这样。

If i request another page all the HttpApplication's created (and their moduless) will again try to serve the request causing it to hit the eventhandler multiple times.

如果我请求另一个页面,所有创建的HttpApplication(以及它们的无模块)将再次尝试提供请求,使其多次命中eventhandler。

But it also seems that if i then jump back to the first page (or another one) only one HttpApplication will start to take care of the request and everything is as expected - as long as i dont let it hang at a break point.

但似乎如果我然后跳回到第一页(或另一个),只有一个HttpApplication将开始处理请求,一切都按预期 - 只要我不让它挂在一个断点。

If i let it hang at a breakpoint it begins to create new HttpApplication's objects and starts adding HttpApplications (more than 1) to serve/handle the request (which is already in process of being served by the HttpApplication which is currently stopped at the breakpoint).

如果我让它在断点处挂起,它就会开始创建新的HttpApplication对象,并开始添加HttpApplications(超过1个)来提供/处理请求(已经在HttpApplication服务的过程中,该HttpApplication当前在断点处停止) 。

I guess or hope that it might be some intelligent "behind the scenes" way of helping to distribute and handle load and / or errors. But I have no clue. I hope some out there can assure me that it is perfectly fine and how it is supposed to be?

我想或者希望它可能是一种帮助分发和处理负载和/或错误的智能“幕后”方式。但我不知道。我希望有些人可以向我保证,它完全没问题,它应该是怎样的?

4 个解决方案

#1


7  

  1. Inspect the HttpContext.Current.Request to see, for what request the module's init is fired. Could be browser sending multiple request.

    检查HttpContext.Current.Request以查看模块的init被触发的请求。可能是浏览器发送多个请求。

  2. If you are connected to IIS, do check IIS logs to know whether any request is received for the time you are staying at the break point.

    如果您已连接到IIS,请检查IIS日志以了解您在断点处停留时是否收到任何请求。

#2


40  

It's normal for the Init() method to be called multiple times. When an application starts up, the ASP.NET Worker process will instantiate as many HttpApplication objects as it thinks it needs, then it'll pool them (eg. resuse them for new requests, similar to database connection pooling).

Init()方法被多次调用是正常的。当应用程序启动时,ASP.NET Worker进程将实例化它认为需要的HttpApplication对象,然后它将它们汇集(例如,将它们重新用于新请求,类似于数据库连接池)。

Now for each HttpApplication object, it will also instantiate one copy of each IHttpModule that is registered and call the Init method that many times. So if 5 HttpApplication objects are created, 5 copies of your IHttpModule will be created, and your Init method called 5 times. Make sense?

现在,对于每个HttpApplication对象,它还将实例化已注册的每个IHttpModule的一个副本,并多次调用Init方法。因此,如果创建了5个HttpApplication对象,将创建5个IHttpModule副本,并且您的Init方法调用5次。合理?

Now why is it instantiating 5 HttpApplications objects say? Well maybe your ASPX page has links to other resources which your browser will try to download, css, javascript, WebResource.aspx, maybe an iframe somewhere. Or maybe the ASP.NET Worker Process 'is in the mood' for starting more than 1 HttpApplication object, that's really an internal detail/optimisation of the ASP.NET process running under IIS (or the VS built in webserver).

现在为什么它实例化5个HttpApplications对象呢?好吧,也许您的ASPX页面链接到您的浏览器将尝试下载的其他资源,css,javascript,WebResource.aspx,也许是某个地方的iframe。或者,ASP.NET Worker Process可能会“启动”超过1个HttpApplication对象,这实际上是在IIS(或内置于Web服务器中的VS)下运行的ASP.NET进程的内部细节/优化。

If you want code that's guaranteed to run just once (and don't want to use the Application_StartUp event in the Global.asax), you could try the following in your IHttpModule:

如果您希望保证只运行一次的代码(并且不想在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;
            }
        }
    }
}

I've done something similar and it seems to work, though I'd welcome critiques of my work in case I've missed something.

我已经做了类似的事情似乎工作,但我欢迎批评我的工作,以防我错过了什么。

#3


2  

Here is a bit of explanation as to what you should use, when, and how they work. When to use Application_Start vs Init in Global.asax?

这里有一些关于你应该使用什么,什么时候以及它们如何工作的解释。何时在Global.asax中使用Application_Start vs Init?

Edit: More reading

编辑:更多阅读

The ASP Column: HTTP Modules

ASP列:HTTP模块

INFO: Application Instances, Application Events, and Application State in ASP.NET

信息:ASP.NET中的应用程序实例,应用程序事件和应用程序状态

#4


1  

Examle above locks the IHttpModule for all requests, and then, it frezes the whole application. If your IHttpModule calls request several times is needed to call HttpApplication method CompleteRequest and dispose the HttpApplication instance of the IHttpModule in EndRequest event in order to remove instance of the HttpApplication like this:

上面的Examle为所有请求锁定IHttpModule,然后,它会冻结整个应用程序。如果您的IHttpModule调用请求需要多次调用HttpApplication方法CompleteRequest并在EndRequest事件中配置IHttpModule的HttpApplication实例,以便删除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
    }

If you need that IHttpModule requests every time without rerequest on postback use this code above.

如果您每次都需要IHttpModule请求而无需在回发时重新请求,请使用上面的代码。

#1


7  

  1. Inspect the HttpContext.Current.Request to see, for what request the module's init is fired. Could be browser sending multiple request.

    检查HttpContext.Current.Request以查看模块的init被触发的请求。可能是浏览器发送多个请求。

  2. If you are connected to IIS, do check IIS logs to know whether any request is received for the time you are staying at the break point.

    如果您已连接到IIS,请检查IIS日志以了解您在断点处停留时是否收到任何请求。

#2


40  

It's normal for the Init() method to be called multiple times. When an application starts up, the ASP.NET Worker process will instantiate as many HttpApplication objects as it thinks it needs, then it'll pool them (eg. resuse them for new requests, similar to database connection pooling).

Init()方法被多次调用是正常的。当应用程序启动时,ASP.NET Worker进程将实例化它认为需要的HttpApplication对象,然后它将它们汇集(例如,将它们重新用于新请求,类似于数据库连接池)。

Now for each HttpApplication object, it will also instantiate one copy of each IHttpModule that is registered and call the Init method that many times. So if 5 HttpApplication objects are created, 5 copies of your IHttpModule will be created, and your Init method called 5 times. Make sense?

现在,对于每个HttpApplication对象,它还将实例化已注册的每个IHttpModule的一个副本,并多次调用Init方法。因此,如果创建了5个HttpApplication对象,将创建5个IHttpModule副本,并且您的Init方法调用5次。合理?

Now why is it instantiating 5 HttpApplications objects say? Well maybe your ASPX page has links to other resources which your browser will try to download, css, javascript, WebResource.aspx, maybe an iframe somewhere. Or maybe the ASP.NET Worker Process 'is in the mood' for starting more than 1 HttpApplication object, that's really an internal detail/optimisation of the ASP.NET process running under IIS (or the VS built in webserver).

现在为什么它实例化5个HttpApplications对象呢?好吧,也许您的ASPX页面链接到您的浏览器将尝试下载的其他资源,css,javascript,WebResource.aspx,也许是某个地方的iframe。或者,ASP.NET Worker Process可能会“启动”超过1个HttpApplication对象,这实际上是在IIS(或内置于Web服务器中的VS)下运行的ASP.NET进程的内部细节/优化。

If you want code that's guaranteed to run just once (and don't want to use the Application_StartUp event in the Global.asax), you could try the following in your IHttpModule:

如果您希望保证只运行一次的代码(并且不想在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;
            }
        }
    }
}

I've done something similar and it seems to work, though I'd welcome critiques of my work in case I've missed something.

我已经做了类似的事情似乎工作,但我欢迎批评我的工作,以防我错过了什么。

#3


2  

Here is a bit of explanation as to what you should use, when, and how they work. When to use Application_Start vs Init in Global.asax?

这里有一些关于你应该使用什么,什么时候以及它们如何工作的解释。何时在Global.asax中使用Application_Start vs Init?

Edit: More reading

编辑:更多阅读

The ASP Column: HTTP Modules

ASP列:HTTP模块

INFO: Application Instances, Application Events, and Application State in ASP.NET

信息:ASP.NET中的应用程序实例,应用程序事件和应用程序状态

#4


1  

Examle above locks the IHttpModule for all requests, and then, it frezes the whole application. If your IHttpModule calls request several times is needed to call HttpApplication method CompleteRequest and dispose the HttpApplication instance of the IHttpModule in EndRequest event in order to remove instance of the HttpApplication like this:

上面的Examle为所有请求锁定IHttpModule,然后,它会冻结整个应用程序。如果您的IHttpModule调用请求需要多次调用HttpApplication方法CompleteRequest并在EndRequest事件中配置IHttpModule的HttpApplication实例,以便删除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
    }

If you need that IHttpModule requests every time without rerequest on postback use this code above.

如果您每次都需要IHttpModule请求而无需在回发时重新请求,请使用上面的代码。