¿Qué sucede en BeginProcessRequest ()?

Estamos utilizando NewRelic para proporcionar rastreos de aplicaciones del lado del servidor.

Hemos notado que algunas de nuestras aplicaciones gastan de manera consistente aproximadamente 100 ms en el método System.Web.Mvc.MvcHandler.BeginProcessRequest() .

Esto ocurre antes de que se llame a cualquier código de controlador personalizado (que se registra por separado, y no de forma acumulativa): no es obvio por qué se gastaría tanto tiempo en este método.

¿Qué tipo de cosas hará MVC en este método? ¿Podría esto simplemente solicitar cola?

[EDIT:] Como era de sospecha, la respuesta de Scalayer a continuación fue puntual. Eliminamos y optimizamos todas nuestras dependencias de sesión, y vimos un aumento masivo en la escalabilidad y estabilidad de la aplicación

Lo que puede estar viendo se conoce comúnmente como Agilidad de subprocesos en .NET.

Lo que probablemente esté viendo en cuanto a los resultados debajo de la etiqueta tópica (es decir, el código de la aplicación en System.Web.HttpApplication.BeginRequest() ) es un problema de agilidad de hilos; en la mayoría de los casos, el tiempo que ve aquí no es necesariamente el código que se está ejecutando, sino el contexto web que espera que los hilos se le vuelvan a liberar desde un locking lector-escritor.

La “pausa” Application_BeginRequest() es bastante común en una stack web de ASP.NET. En general, cuando ve tiempos de carga largos en BeginRequest, se trata de agilidad de subprocesos ASP.NET y / o lockings de subprocesos, especialmente cuando se trata de IO y operaciones basadas en sesiones. No es que sea malo, así es como .net se asegura de que tus hilos permanezcan concurrentes.

El intervalo de tiempo generalmente ocurre entre BeginRequest y PreRequestHandlerExecute. Si la aplicación está escribiendo varias cosas para la sesión, ASP.NET emitirá un locking de lector-escritor en HttpContext.Current.Session .

Una buena forma de ver si este es un problema que podría enfrentar sería verificar los ID de los hilos para ver si la agilidad es un problema: los ID serán diferentes para una solicitud determinada.

Por ejemplo. Mientras realiza la depuración, quizás podría agregar lo siguiente a su Global.asax.cs :

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

Abra la ventana de salida de depuración (desde Visual Studio: Ver >> Salida, luego seleccione “Depurar” desde el menú desplegable “mostrar salida desde”).

Mientras realiza la depuración, acceda a una página donde haya visto el tiempo prolongado. Luego, vea el registro de salida: si ve varios identificadores, es posible que sufra esto.

Esta es la razón por la que puede ver el retraso algunas veces, pero otras veces, el código de la aplicación podría estar usando la sesión de forma diferente o las operaciones de sesión o IO podrían ser mayores o menores de una página a otra.

Si este es el caso, puede hacer algunas cosas para ayudar a acelerar las cosas dependiendo de cómo se usa la sesión en el sitio o en cada página.

Para las páginas que no modifican la sesión:

  <% @Page EnableSessionState="ReadOnly" %> 

Para páginas que no usan estado de sesión:

 <% @Page EnableSessionState="False" %> 

Si la aplicación no usa session (web.config):

      

Tomemos el siguiente ejemplo:

El usuario carga una página, luego decide ir a otra página antes de que la primera solicitud finalice. Al cargar ASP.NET, se forzará un locking de sesión que causará que la nueva carga de solicitud de página espere hasta que finalice la primera solicitud de la página. Con ASP.NET MVC, cada acción bloquea la sesión del usuario para la sincronización; causando el mismo problema

Todo el tiempo necesario para que se libere la cerradura se informará a través de una nueva reliquia, por no mencionar aquellos en los que el usuario abandonó la sesión y el hilo que vuelve busca un usuario que ya no existe.

Por cierto, el control UpdatePanel causa el mismo comportamiento:

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

Qué se puede hacer:

Este problema de locking es uno de los motivos por los que Microsoft tiene la clase SessionStateUtility:

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

Para que pueda anular el comportamiento predeterminado si enfrenta este problema como se ve aquí en esta implementación de Redis: https://github.com/angieslist/AL-Redis

Hay muchas opciones para el proveedor de estado predeterminado utilizado por los sitios web basados ​​en .net. Pero sepa en general que este tiempo de transacción indica que los hilos están siendo bloqueados y esperando que se completen las solicitudes al servidor.

Si desea habilitar un determinado controlador para procesar solicitudes en paralelo desde un solo usuario, puede usar un atributo llamado SessionState que se introdujo en MVC desde la versión 3. No es necesario usar un controlador sin sesión para lograr el paralelismo. la opción Ready-only de SessionStateBehavior le permitirá pasar comprobaciones de seguridad que puede haber implementado en función de los datos de sesión.

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

También tuve retrasos en System.Web.Mvc.MvcHandler.BeginProcessRequest() , cuando bash hacer algunas acciones largas y lo vi en NewRelic . Estos atributos han resuelto el problema y le han permitido manejar acciones paralelas para este controlador.

Encontré este mismo problema en una aplicación que tenía un uso mínimo de sesiones. Después de tener en cuenta la respuesta aceptada e incluso de eliminar temporalmente SessionState por completo con fines de diagnóstico, todavía tenía problemas significativos con el rendimiento en este ‘Método’.

Basándome en el comentario de todd_saylor en este hilo , investigué mucho en mi propio sitio y encontré que BeginProcessRequest puede servir como el nuevo catch-all de New Relic para pérdidas de rendimiento misceláneas que aparecen en diferentes áreas de código. Pude reducir significativamente el tiempo en esta área, al crear un código de perfil en mi máquina local utilizando ANTs Performance Profiler de Red Gate.

Mi perfil local reveló algunas cosas:

  1. Estaba usando Ninject como mi contenedor DI, lo que causó la pérdida de rendimiento en su resolución de objeto. Lo reemplacé con SimpleInjector y aumenté el rendimiento en un orden de magnitud.
  2. Encontré que en algún lugar entre el Project().To<>() Extensiones IQueryable de AutoMapper Project().To<>() y el proveedor del Servidor Sql de Linq, que la comstackción dinámica y JITing de las proyecciones era responsable del 50% de mi tiempo de solicitud. Reemplazar eso con CompiledQuerys hizo otra mella significativa.

¡Espero que esto ayude a alguien más!

Para cualquiera que esté leyendo esto y experimente un alto uso de la CPU de IIS que no tiene explicación, eche un vistazo a este hilo del foro de soporte de NewRelic que abrí.

He estado lidiando con este problema durante más de una semana, hasta que me di cuenta de que era su agente el origen del problema.

Agente .NET que causa un uso elevado de CPU en IIS

entonces, lo primero que sugiero que intente es eliminar el agente .NET y ver si hay algún cambio, antes de sumergirse en experimentos más complejos.

Publiqué esta respuesta sobre esta pregunta en particular porque llegué aquí primero mientras trabajaba en el tema, y ​​la agilidad de la secuencia me puso en un curso largo que no era correcto … (aunque muy interesante en sí mismo).