2013-06-12 4 views
56

Stiamo usando NewRelic per fornire tracce di applicazioni lato server.Cosa succede in BeginProcessRequest()?

Abbiamo notato che alcune delle nostre applicazioni spendono costantemente circa 100 ms nel metodo System.Web.Mvc.MvcHandler.BeginProcessRequest().

Questo accade prima che venga chiamato qualsiasi codice del controller personalizzato (che viene registrato separatamente e non cumulativamente) - non è ovvio perché trascorrerà così tanto tempo in questo metodo.

Che tipo di cose farà MVC in questo metodo? Potrebbe essere semplicemente una richiesta in coda?

[EDIT:] Come sospetto - La risposta di Scalayer di seguito era azzeccata. Abbiamo rimosso & ottimizzati tutti i nostri dipendenze di sessione, e ho visto un massiccio aumento della scalabilità delle applicazioni & stabilità

+0

http://blog.stevensanderson.com/blogfiles/2007/ASPNET-MVC-Pipeline/ASP.NET%20MVC%20Pipeline.pdf –

+0

@Ravi BeginRequest() non è lì! :( –

+0

Stai usando gestori asincroni (IHttpAsyncHandler)? –

risposta

84

Che cosa si potrebbe vedere che viene comunemente indicato come l'agilità filo in .NET.

Quello che si sta probabilmente vedendo fino ai risultati al di sotto dell'etichetta topica (ad esempio il codice dell'applicazione in System.Web.HttpApplication.BeginRequest()) è un problema di agilità del thread; nella maggior parte dei casi il tempo che vedete qui non è necessariamente il codice che viene eseguito, ma il contesto web in attesa che i thread vengano rilasciati da un blocco di lettore-scrittore.

La "pausa" Application_BeginRequest() è uno che è abbastanza pervasivo in uno stack Web ASP.NET. In generale, quando si visualizzano tempi di caricamento lunghi in BeginRequest, si ha a che fare con agilità di thread ASP.NET e/o blocchi di thread, specialmente quando si ha a che fare con operazioni IO e basate su sessioni. Non che sia una brutta cosa, questo è solo il modo .net assicura che i tuoi thread rimangano concomitanti.

Il divario di tempo si verifica in genere tra BeginRequest e PreRequestHandlerExecute. Se l'applicazione sta scrivendo diverse cose per la sessione, ASP.NET emetterà un blocco lettore-scrittore su HttpContext.Current.Session.

Un buon modo per vedere se questo è un problema che si potrebbe affrontare sarebbe quello di controllare gli ID del thread per vedere se l'agilità è un problema - gli ID saranno diversi per una determinata richiesta.

Per esempio. Durante il debug, forse si potrebbe aggiungere quanto segue al Global.asax.cs:

protected void Application_BeginRequest(Object sender, EventArgs e) { 
     Debug.WriteLine("BeginRequest_" + Thread.CurrentThread.ManagedThreadId.ToString()); 
    } 

aprire la finestra di output di debug (da Visual Studio: View >> uscita, quindi selezionare "Debug" dal "spettacolo di uscita dal" menu a discesa) .

Durante il debug, premere una pagina in cui si è visto il tempo lungo. Quindi visualizza il log di output: se vedi più ID, potresti soffrire di questo.

Questo è il motivo per cui a volte è possibile vedere il ritardo ma non altre volte, il codice dell'applicazione potrebbe utilizzare la sessione in modo leggermente diverso o le operazioni di I/O potrebbero essere più alte o più basse da una pagina all'altra.

Se questo è il caso, alcune cose che puoi fare per velocizzare le cose a seconda di come la sessione viene usata sul sito o su ciascuna pagina.

Per le pagine che non modificano sessione:

<% @Page EnableSessionState="ReadOnly" %> 

Per le pagine che non utilizzano sessione di Stato:

<% @Page EnableSessionState="False" %> 

Se l'applicazione non fa uso di sessione (web.config):

Quindi prendiamo il seguente esempio:

L'utente carica una pagina, quindi decide di passare a un'altra pagina prima che la prima richiesta venga completata caricando ASP.NET imporrà un blocco di sessione che causa il caricamento della nuova pagina di attesa per il completamento della richiesta della prima pagina. Con ASP.NET MVC ogni azione blocca la sessione utente per la sincronizzazione; causando lo stesso problema.

Tutto il tempo necessario per il rilascio del blocco verrà segnalato tramite nuova reliquia, per non parlare di quelli in cui l'utente ha abbandonato la sessione e il thread che sta tornando sta cercando un utente che non esiste più.

Tra l'altro il controllo UpdatePanel provoca lo stesso comportamento -

http://msdn.microsoft.com/en-us/magazine/cc163413.aspx

cosa si può fare:

Questo problema di blocco è uno dei motivi che Microsoft ha la classe SessionStateUtility -

http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstateutility.aspx

In modo da poter ignorare il comportamento predefinito r se si affronta questo problema come si vede qui in questa implementazione Redis : https://github.com/angieslist/AL-Redis

Ci sono molte opzioni per il provider di stato predefinito utilizzato dai siti Web basati su .net. Ma in genere questo tempo di transazione indica che i thread vengono bloccati e in attesa di completamento delle richieste al server.

+2

Grazie per una risposta così fantastica a questa domanda - Ho la netta sensazione che tu abbia ragione, come nelle pagine in questione - È possibile che eseguiamo un numero elevato di operazioni di sessione ingombranti. –

+0

Ho appena provato questo, dal momento che ho riscontrato un problema simile. Quando si dice "se si visualizzano più ID, si potrebbe soffrire di questo" intendi più ID sulla stessa richiesta o solo un ID diverso per ogni richiesta? – amhed

+1

Una buona risposta, molte delle quali (e più) possono essere trovate [in questa domanda correlata] (http://stackoverflow.com/questions/3629709/i-giusto-discovered-why-all-asp-net -noi bsites-are-slow-and-i-am-trying-to-work-out) – Co7e

6

Se si desidera abilitare un determinato controller per elaborare richieste in parallelo da un singolo utente, è possibile utilizzare un attributo denominato SessionState introdotto in MVC dalla versione 3. Non è necessario utilizzare un controller senza sessione in ordine per ottenere il parallelismo, l'opzione Solo pronto di SessionStateBehavior consente di superare i controlli di sicurezza che potrebbero essere implementati in base ai dati di sessione.

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)] 
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")] 
public class ParallelController : Controller 
{ 
    ... 
} 

Ho anche avuto ritardi nella System.Web.Mvc.MvcHandler.BeginProcessRequest(), quando provo a fare alcune azioni lunga corsa e ho visto in NewRelic. Questi attributi hanno risolto il problema e hanno dato la possibilità di gestire azioni parallele per questo controller.

0

a chiunque stia leggendo questo elevato utilizzo della CPU di IIS che è inspiegabile, date un'occhiata a questa discussione dal forum di supporto di NewRelic che ho aperto.

Ho avuto a che fare con questo problema per oltre una settimana, fino a quando ho capito che era il loro agente che era la causa principale del problema.

.NET agent causing high CPU usage on IIS

così prima cosa che vi consiglio di provare è la rimozione del.Agente NET e vedere se c'è qualche cambiamento, prima di tuffarti in esperimenti più complessi.

Ho postato questa risposta su questa particolare domanda da quando sono arrivato qui prima mentre lavoravo al problema, e l'agilità del thread mi ha impostato su un percorso lungo che non era corretto ... (anche se di per sé molto interessante).

2

Ho riscontrato questo stesso problema in un'app che ha avuto un utilizzo minimo delle sessioni. Dopo aver preso in considerazione la risposta accettata e persino rimosso temporaneamente SessionState per scopi diagnostici, ho riscontrato problemi significativi nelle prestazioni in questo "Metodo"

In base al commento di todd_saylor in this thread, ho svolto molte ricerche per conto mio. sito e ha rilevato che BeginProcessRequest può essere utilizzato come il nuovo catch di New Relic per le perdite di prestazioni varie che appaiono in aree di codice diverse. Sono stato in grado di ridurre significativamente il tempo in quest'area, profilando il codice sulla mia macchina locale utilizzando ANTs Performance Profiler di Red Gate.

mio profilazione locale ha rivelato un paio di cose:

  1. stavo usando Ninject come il mio contenitore DI, che ha causato la perdita di prestazioni nella sua risoluzione oggetto. L'ho sostituito con SimpleInjector e ho aumentato le prestazioni di un ordine di grandezza.
  2. Ho trovato che da qualche parte tra le estensioni IQueryable di AutoMapper Project().To<>() e il provider di SQL Server di Linq, che la compilazione dinamica e JITing delle proiezioni era responsabile per il 50% del tempo di richiesta. Sostituire quella con CompiledQuerys ha fatto un'altra ammaccatura significativa.

Spero che questo aiuti qualcun altro!