web-dev-qa-db-fra.com

Que se passe-t-il dans BeginProcessRequest ()?

Nous utilisons NewRelic pour fournir des traces d'application côté serveur.

Nous avons remarqué que certaines de nos applications passent régulièrement environ 100 ms dans la méthode System.Web.Mvc.MvcHandler.BeginProcessRequest().

Cela se produit avant d'appeler un code de contrôleur personnalisé (qui est enregistré séparément et non cumulativement) - il n'est pas évident pourquoi il passerait autant de temps dans cette méthode.

Quels types de choses MVC fera-t-il avec cette méthode? Serait-ce simplement une demande de mise en file d'attente?

[EDIT:] Comme suspect - la réponse de Scalayer ci-dessous était parfaite. Nous avons supprimé et optimisé toutes nos dépendances de session, et nous avons vu une augmentation massive de l'évolutivité et de la stabilité des applications

58
Dave Bish

Ce que vous pourriez voir est communément appelé agilité des threads dans .NET.

Ce que vous voyez probablement en ce qui concerne les résultats sous l'étiquette d'actualité (c'est-à-dire le code d'application dans System.Web.HttpApplication.BeginRequest()) est un problème d'agilité de thread; dans la plupart des cas, le temps que vous voyez ici n'est pas nécessairement du code en cours d'exécution, mais le contexte Web attendant que les threads lui soient libérés à partir d'un verrou de lecture/écriture.

La Application_BeginRequest() "pause" est assez généralisée dans une pile Web ASP.NET. En général, lorsque vous voyez de longs temps de chargement dans BeginRequest, vous avez affaire à l'agilité des threads ASP.NET et/ou aux verrous de threads - en particulier lorsque vous traitez avec IO et les opérations basées sur la session. Ce n'est pas une mauvaise C'est juste comment .net s'assure que vos threads restent simultanés.

L'écart de temps se produit généralement entre BeginRequest et PreRequestHandlerExecute. Si l'application écrit plusieurs choses dans la session, ASP.NET émettra un verrou de lecture/écriture sur HttpContext.Current.Session.

Un bon moyen de voir s'il s'agit d'un problème auquel vous pourriez être confronté serait de vérifier les ID de threads pour voir si l'agilité est un problème - les ID seront différents pour une demande donnée.

Par exemple. Pendant le débogage, vous pouvez peut-être ajouter ce qui suit à votre Global.asax.cs:

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

Ouvrez la fenêtre de sortie de débogage (à partir de Visual Studio: Affichage >> Sortie, puis sélectionnez "Déboguer" dans la liste déroulante "afficher la sortie de").

Pendant le débogage, cliquez sur une page où vous avez vu depuis longtemps. Ensuite, affichez le journal de sortie - si vous voyez plusieurs identifiants, vous pourriez en souffrir.

C'est pourquoi vous pouvez voir le retard parfois mais pas d'autres fois, le code d'application peut utiliser la session un peu différemment ou la session ou les opérations IO peuvent être supérieures ou inférieures d'une page à l'autre).

Si tel est le cas, certaines choses que vous pouvez faire pour accélérer les choses en fonction de la façon dont la session est utilisée sur le site ou sur chaque page donnée.

Pour les pages qui ne modifient pas la session:

   <% @Page EnableSessionState="ReadOnly" %>

Pour les pages qui n'utilisent pas l'état de session:

<% @Page EnableSessionState="False" %>

Si l'application n'utilise pas de session (web.config):

<configuration>
    <system.web>
      <sessionState mode="Off" />
    </system.web>
</configuration>

Prenons donc l'exemple suivant:

L'utilisateur charge une page, puis décide d'accéder à une autre page avant la fin de la première demande. Le chargement d'ASP.NET force un verrouillage de session, ce qui fait attendre le chargement de la nouvelle demande de page jusqu'à la fin de la première demande de page. Avec ASP.NET MVC, chaque action verrouille la session utilisateur pour la synchronisation; causant le même problème.

Tout le temps qu'il a fallu pour que le verrou soit libéré sera signalé via une nouvelle relique, sans parler de ceux où l'utilisateur a abandonné la session et le thread qui revient cherche un utilisateur qui n'existe plus.

Par ailleurs, le contrôle UpdatePanel provoque le même comportement -

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

Ce qui peut être fait:

Ce problème de verrouillage est l'une des raisons pour lesquelles Microsoft a la classe SessionStateUtility -

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

Afin que vous puissiez remplacer le comportement par défaut si vous rencontrez ce problème comme vu ici dans cette implémentation de Redis: https://github.com/angieslist/AL-Redis

Il existe de nombreuses options pour le fournisseur d'état par défaut utilisé par les sites Web basés sur .net. Mais sachez généralement que ce temps de transaction indique que les threads sont verrouillés et attendent que les demandes au serveur soient terminées.

86
Scalayer

Si vous souhaitez permettre à un certain contrôleur de traiter les demandes en parallèle d'un seul utilisateur, vous pouvez utiliser un attribut nommé SessionState qui a été introduit dans MVC depuis la version 3. Il n'est pas nécessaire d'utiliser un contrôleur sans session pour obtenir le parallélisme, l'option Ready-only de SessionStateBehavior vous permettra de passer des contrôles de sécurité que vous pourriez avoir mis en œuvre sur la base des données de session.

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

J'ai également eu des retards dans System.Web.Mvc.MvcHandler.BeginProcessRequest(), lorsque j'ai essayé de faire quelques actions longues et je l'ai vu dans NewRelic. Ces attributs ont résolu le problème et ont permis de gérer des actions en parallèle pour ce contrôleur.

6
Andrey Zhminka

J'ai rencontré ce même problème dans une application qui utilisait très peu de sessions. Après avoir pris en compte la réponse acceptée, et même supprimé temporairement SessionState à des fins de diagnostic, j'ai toujours eu des problèmes importants avec les performances de cette "méthode"

Sur la base des commentaires de todd_saylor dans ce fil , j'ai fait beaucoup de recherches sur mon propre site et j'ai découvert que BeginProcessRequest peut servir de fourre-tout à New Relic pour diverses pertes de performances qui apparaissent dans différentes zones de code. J'ai pu réduire considérablement le temps dans ce domaine, en profilant le code sur ma machine locale en utilisant ANTs Performance Profiler de Red Gate.

Mon profilage local a révélé quelques choses:

  1. J'utilisais Ninject comme conteneur DI, ce qui a entraîné une perte de performances dans sa résolution d'objet. Je l'ai remplacé par SimpleInjector et augmenté les performances d'un ordre de grandeur.
  2. J'ai constaté que quelque part entre les extensions IQueryable d'AutoMapper Project().To<>() et le fournisseur de serveur Sql de Linq, que la compilation dynamique et le JITing des projections étaient responsables de 50% du temps de ma demande. Remplacer cela par CompiledQuerys a fait une autre percée importante.

J'espère que ceci aide quelqu'un d'autre!

2
Matt Murrell

à tous ceux qui lisent cette expérience IIS utilisation élevée du processeur inexpliquée, jetez un œil à ce fil du forum de support NewRelic que j'ai ouvert.

Je m'occupe de ce problème depuis plus d'une semaine, jusqu'à ce que je réalise que c'était leur agent qui était à l'origine du problème.

agent .NET provoquant une utilisation élevée du processeur sur IIS

donc la première chose que je vous suggère d'essayer est de supprimer l'agent .NET et de voir s'il y a un changement, avant de plonger dans des expériences plus complexes.

Je poste cette réponse sur cette question particulière depuis que je suis arrivé ici en travaillant sur le problème, et l'agilité des threads m'a mis sur un long parcours qui n'était pas correct ... (bien que très intéressant en soi).

0
Ami