2009-07-17 7 views
30

Ich erstellte ein HTTP-Modul und während des Debugging bemerkte ich etwas, das zunächst (zumindest) wie seltsames Verhalten schien.HttpModule Init-Methode wird mehrmals aufgerufen - warum?

Wenn ich einen Haltepunkt in der Init-Methode des httpmodul setze, kann ich sehen, dass die HTTP-Modul-Init-Methode mehrmals aufgerufen wird, obwohl ich nur die Website zum Debuggen gestartet und eine einzige Anfrage (manchmal ist es) Treffer nur 1 Mal, andere Male bis zu 10 Mal).

Ich weiß, dass ich mehrere Instanzen der HttpApplication erwarten sollte und für jeden der HTTP-Module erstellt werden, aber wenn ich eine einzelne Seite anfordert, sollte es von einem einzigen HTTP-Anwendungsobjekt behandelt werden und daher nur die Ereignisse, die einmal verknüpft sind, aber dennoch die Ereignisse mehrmals für jede Anfrage auslösen, was keinen Sinn ergibt - abgesehen davon, dass sie innerhalb dieser httpApplication mehrmals hinzugefügt wurden - was bedeutet, dass es dieselbe httpmodul-Init-Methode ist, die jedes Mal aufgerufen wird Eine neue http-Anwendung wird jedes Mal erstellt, wenn sie auf meinen Breakpoint trifft (siehe mein Codebeispiel unten).

Was könnte hier schiefgehen? liegt es daran, dass ich debuggen und einen Haltepunkt im HTTP-Modul setze?

Es hat festgestellt, dass es scheint, dass, wenn ich die Website für das Debuggen starten und schnell über den Haltepunkt in der HTTPModule geht es nur die Init-Methode trifft nur einmal und das gleiche gilt für den Eventhandler. Wenn ich es stattdessen für ein paar Sekunden am Haltepunkt hängen lasse, wird die init-Methode mehrmals aufgerufen (scheint wie es hängt davon ab, wie lange ich warte, bevor ich über den Haltepunkt gehe). Vielleicht könnte dies ein eingebautes Feature sein, um sicherzustellen, dass das HTTP-Modul initialisiert wird und die HTTP-Anwendung Anfragen bedienen kann, aber es scheint auch etwas zu sein, das katastrophale Folgen haben könnte.

Dies könnte logisch erscheinen, da es möglicherweise versucht, die Anfrage zu beenden, und da ich den Unterbrechungspunkt gesetzt habe, denkt es, dass etwas falsch gelaufen ist und versuche, die init-Methode erneut aufzurufen? Soo kann es die Anfrage bearbeiten?

Aber ist das was passiert und ist alles in Ordnung (ich rate nur), oder ist es ein echtes Problem?

Was mich besonders beunruhigt ist, dass, wenn etwas es auf dem Server "production/live" für ein paar Sekunden hängt eine Menge Event-Handler durch die init hinzugefügt werden und daher jede Anfrage auf der Seite plötzlich den Eventhandler auslöst mehrmals.

Dieses Verhalten könnte schnell jede Website herunterbringen.

Ich habe den "Original" .net-Code für die httpmodules für formsauthentication und das rolemanagermodul etc verwendet, aber mein Code ist nicht anders, dass diese Module verwendet.

Mein Code sieht so aus.

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

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

hier ist ein Beispiel, wie es in der Rolemanagermodule aus dem .NET-Framework

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

jemand getan wird Sie wissen, was los ist?

(ich hoffe, dass nur jemand da draußen kann mir sagen, warum dies geschieht und versichern mir, dass alles völlig in Ordnung ist) :)


UPDATE:

Ich habe versucht, das Problem zu verengen und bis jetzt habe ich festgestellt, dass die aufgerufene Init-Methode immer auf einem neuen Objekt meines http-Moduls (im Gegensatz zu dem, was ich vorher dachte) ist.

Ich scheint, dass für die erste Anfrage (beim Starten der Website) alle HttpApplication-Objekte erstellt werden und seine Module alle versuchen, die erste Anfrage zu bedienen und daher alle den Eventhandler, der hinzugefügt wird. Ich kann wirklich nicht herausfinden, warum das passiert.

Wenn ich eine andere Seite anfordere, versuchen alle (und ihre moduless) erzeugten HttpApplication erneut, die Anfrage zu liefern, die bewirkt, dass sie den Eventhandler mehrfach trifft.

Aber es scheint auch, dass, wenn ich dann auf die erste Seite (oder eine andere) zurückspringen nur eine HttpApplication beginnt, um die Anfrage zu kümmern und alles ist wie erwartet - solange ich es nicht bei einem hängen lassen Haltepunkt.

Wenn ich es an einem Haltepunkt hängen lasse, fängt es an, neue HttpApplication-Objekte zu erstellen und startet HttpApplications (mehr als 1), um die Anfrage zu bedienen (die gerade von der HttpApplication bedient wird, die gerade gestoppt wird) am Haltepunkt).

Ich denke oder hoffe, dass es einige intelligente "hinter den Kulissen" Weg der Verteilung und Handhabung von Last und/oder Fehler helfen könnte. Aber ich habe keine Ahnung. Ich hoffe, dass einige da draußen mir versichern können, dass es vollkommen in Ordnung ist und wie es sein soll?

+2

Ich sehe dieses Verhalten in unserem HttpModule sowie –

+0

John: Ich habe vergessen, dieses zu schließen. Es passiert (zumindest in meinem Fall), weil das Modul von jeder einzelnen Anfrage, die der Browser für eine Ressource (Bilder, Javascripte, Stylesheets) stellt, getroffen wird. Der Grund, warum es getroffen wird, ist das Routing-Modul in etc MVC verwendet. Alle Anfragen müssen vom Routing-Modul bearbeitet werden, weshalb alle Anfragen das Modul unglücklich treffen. Ich habe eine Frage dazu gestellt, aber niemand hat darauf geantwortet. – MartinF

Antwort

7
  1. Überprüfen Sie die HttpContext.Current.Request zu sehen, für das, was abgefragt wird das Modul des init gefeuert. Könnte Browser mehrere Anfrage senden.

  2. Wenn Sie mit IIS verbunden sind, überprüfen Sie die IIS-Protokolle, um zu wissen, ob für die Zeit, in der Sie am Unterbrechungspunkt bleiben, eine Anforderung eingegangen ist.

+0

Korrigieren. Es passiert, weil alle Anfragen (für irgendeine Ressource) das Modul treffen. In meinem Fall wegen des Routing-Moduls scheint es. – MartinF

2
+0

Danke für Ihre Antwort. Ich benutze keine von ihnen. Init in der globalen asax überschreibt die init in HttpApplication. Mein Problem ist, dass die Init-Methode in meinem httpmodul mehrmals aufgerufen wird. Soweit ich es verstehe, sollte es die HTTP-Module für jede http-Anwendung erstellen. Bei jeder Anfrage wird einer der instanziierten HttpApplication's die Anfrage bearbeiten und die hinzugefügten Ereignisse auslösen, aber aus irgendeinem Grund wird der eventhandler * manchmal * mehr als einmal als Init-Methode auf demselben HTTP-Modul hinzugefügt (nicht einer der neu erstellten für andere httpapplication) wird mehrmals aufgerufen. – MartinF

+0

Mehr zu lesen hinzugefügt. –

40

Es ist normal, dass die Methode Init() mehrmals aufgerufen wird. Wenn eine Anwendung gestartet wird, instanziiert der ASP.NET Worker-Prozess so viele HttpApplication-Objekte, wie er es für erforderlich hält. Anschließend bündelt er sie (z. B. für neue Anforderungen, ähnlich wie beim Datenbankverbindungs-Pooling).

Jetzt wird für jedes HttpApplication-Objekt auch eine Kopie jedes registrierten IHttpModule instanziiert und die Init-Methode mehrmals aufgerufen. Wenn also 5 HttpApplication-Objekte erstellt werden, werden 5 Kopien Ihres IHttpModule erstellt, und Ihre Init-Methode wird 5 Mal aufgerufen. Sinn ergeben?

Nun, warum wird es instanziieren 5 HttpApplications Objekte sagen? Nun, vielleicht hat Ihre ASPX-Seite Links zu anderen Ressourcen, die Ihr Browser herunterladen wird, CSS, Javascript, WebResource.aspx, vielleicht ein Iframe irgendwo. Oder vielleicht ist der ASP.NET-Worker-Prozess "in der Stimmung", mehr als ein HttpApplication-Objekt zu starten, das ist wirklich eine interne Detail/Optimierung des ASP.NET-Prozesses, der unter IIS (oder dem VS-eingebauten Webserver) läuft.

Wenn Sie Code möchten, die nur einmal ausgeführt garantiert wird (und wollen nicht die Application_Startup Ereignis in der Global.asax verwenden), können Sie die folgenden in Ihrem IHttpModule versuchen:

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‘ Ich habe etwas ähnliches getan und es scheint zu funktionieren, obwohl ich Kritiken meiner Arbeit begrüßen würde, falls ich etwas verpasst habe.

+10

Dies ist der Ort zu verwenden 'Interlocked.CompareExchange' anstelle von Schloss :) –

+1

Erfordert sogar Ressource-Datei verursachen Erstellung von App-Klasse und es ist eigenes Modul, und so Auslösen dieses Ereignisses? –

1

Beispiel oben sperrt das IHttpModule für alle Anfragen, und dann friert es die gesamte Anwendung. Wenn Ihr IHttpModule Anrufe mehrmals anfordern benötigt wird Httpapplication Methode CompleteRequest aufzurufen und die Httpapplication Instanz des IHttpModule in Endrequest-Ereignis, um wie diese Instanz des Httpapplication zu entfernen entsorgen:

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 
    } 

Wenn Sie, dass IHttpModule Anfragen jedes Mal, ohne den Postback erneut anzufordern, benutze diesen Code oben.

+0

Das ist ein guter Punkt, aber für einige Anwendungen (wie meine) ist der auszuführende Code höchstens einige Mikrosekunden, also ist die Sperre definitiv kosteneffektiv. Aber danke für die Idee, ich denke, dies zu wissen, wird meine Programmierung verbessern. – Abacus