2013-06-12 19 views
56

Używamy NewRelic do udostępniania śladów aplikacji po stronie serwera.Co dzieje się w BeginProcessRequest()?

Zauważyliśmy, że niektóre z naszych aplikacji konsekwentnie spędzają około 100 ms w metodzie System.Web.Mvc.MvcHandler.BeginProcessRequest().

Zdarza się to przed wywołaniem niestandardowego kodu kontrolera (który jest rejestrowany oddzielnie, a nie kumulatywnie) - nie jest oczywiste, dlaczego spędziłoby tak dużo czasu w tej metodzie.

Jakie czynności wykona MVC w tej metodzie? Czy może to po prostu wymagać kolejkowania?

[EDIT:] Jak podejrzewano - odpowiedź Scalayera poniżej była spot-on. Usunęliśmy & zoptymalizowana precz wszystkie zależności sesji, i zobaczył ogromny wzrost skalowalności aplikacji & stabilności

+0

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

+0

@Ravi BeginRequest() nie jest tam! :( –

+0

Czy używasz jakichkolwiek procedur obsługi asynchronicznych (IHttpAsyncHandler)? –

Odpowiedz

84

Co może być wyświetlany jest powszechnie określany jako agility wątku w .NET.

To, co prawdopodobnie widzisz aż do wyników pod etykietą miejscową (tj. Kod aplikacji w System.Web.HttpApplication.BeginRequest()) jest problemem zręczności wątku; w większości przypadków czas, który tu widzisz, niekoniecznie jest wykonywany, ale kontekst sieciowy czeka na przywrócenie wątków z blokady czytnika-pisarza.

"Przerwa" Application_BeginRequest() jest bardzo rozpowszechniona w stosie sieci ASP.NET. Generalnie, gdy widzisz długi czas ładowania w BeginRequest, masz do czynienia ze zwinnością wątków ASP.NET i/lub blokadami wątków - szczególnie w przypadku operacji IO i operacji opartych na sesjach. Nie jest to złe, właśnie dlatego .net zapewnia, że ​​wątki pozostaną współbieżne.

Luka czasowa zazwyczaj występuje pomiędzy BeginRequest i PreRequestHandlerExecute. Jeśli aplikacja pisze kilka rzeczy do sesji, ASP.NET wyda blokadę czytnika-pisarza na HttpContext.Current.Session.

Dobrym sposobem sprawdzenia, czy to jest problem, z którym możesz się spotkać, jest sprawdzenie identyfikatorów wątków w celu sprawdzenia, czy sprawność jest problematyczna - identyfikatory będą różne dla danego żądania.

Na przykład. Podczas debugowania, być może można dodać następujące do Global.asax.cs:

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

Otwórz okno wyjściowe debugowania (z Visual Studio: Zobacz >> wyjścia, a następnie wybierz „Debug” z „wyjście pokazu z” listy rozwijanej) .

Podczas debugowania, naciśnij stronę, na której widzieliście od dawna. Następnie przejrzyj dziennik wyjściowy - jeśli zobaczysz wiele identyfikatorów, możesz na tym cierpieć.

Dlatego czasami może wystąpić opóźnienie, ale nie w innych przypadkach, kod aplikacji może nieco inaczej używać sesji lub sesja lub operacje we-wy mogą być wyższe lub niższe w zależności od strony.

W takim przypadku można zrobić kilka rzeczy, aby przyspieszyć działanie, w zależności od tego, w jaki sposób sesja jest używana na stronie lub na danej stronie.

przypadku stron, które nie modyfikują sesji:

<% @Page EnableSessionState="ReadOnly" %> 

przypadku stron, które nie korzystają stanu sesji:

<% @Page EnableSessionState="False" %> 

Jeśli aplikacja nie korzysta z sesji (web.config):

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

Weźmy następujący przykład:

Użytkownik ładuje stronę, a następnie decyduje się przejść do innej strony przed wykonaniem pierwszego żądania. Załadowanie ASP.NET wymusi blokadę sesji powodującą, że nowe żądanie strony ładuje się do momentu zakończenia żądania pierwszej strony. W ASP.NET MVC każda akcja blokuje sesję użytkownika w celu synchronizacji; powodując ten sam problem.

Cały czas potrzebny do wydania blokady zostanie zgłoszony za pośrednictwem nowej relikwii, nie wspominając o tych, w których użytkownik zrezygnował z sesji, a wątek wracający szuka użytkownika, który już nie istnieje.

Nawiasem mówiąc kontrola UpdatePanel powoduje takie samo zachowanie -

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

Co można zrobić:

Problem ten zamek jest jednym z powodów, Microsoft ma klasę SessionStateUtility -

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

Aby można było przesłonić domyślne zachowanie r jeśli napotkasz ten problem, jak widać tutaj w tej Realizacja Redis: https://github.com/angieslist/AL-Redis

Istnieje wiele opcji dla domyślnego dostawcy stanu używanego przez witryny oparte na .net. Ale ogólnie wiadomo, że czas transakcji wskazuje, że wątki są blokowane i oczekują na żądania do serwera, które mają być zakończone.

+2

Dzięki za tak fantastyczną odpowiedź na to pytanie - mam silne przeczucie, że masz rację, jak na stronach, o których mowa - Jest całkiem możliwe, że wykonujemy dużą liczbę nieporęcznych operacji sesyjnych. –

+0

Po prostu próbowałem tego, ponieważ mam podobny problem, kiedy mówisz "jeśli zobaczysz wiele identyfikatorów, to możesz cierpieć z tego powodu" masz na myśli wiele identyfikatorów na tej samej prośbie? Lub po prostu inny identyfikator dla każdego Żądanie? – amhed

+1

Dobra odpowiedź, z której wiele (i więcej) można znaleźć [w tym pokrewnym pytaniu] (http://stackoverflow.com/questions/3629709/just-discovered-why-all-asp-net -my bsites-are-slow-and-i-am-trying-to-work-out) – Co7e

6

Jeśli chcesz, aby dany kontroler przetwarzał żądania równolegle od jednego użytkownika, możesz użyć atrybutu o nazwie SessionState, który został wprowadzony w MVC od wersji 3. Nie jest konieczne użycie kontrolera bez sesji w celu w celu osiągnięcia paralelizmu opcja "SessionStateBehavior" dostępna tylko w trybie gotowości pozwala na sprawdzenie zabezpieczeń, które można wdrożyć na podstawie danych sesji.

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

Miałem też opóźnień w System.Web.Mvc.MvcHandler.BeginProcessRequest(), gdy staram się robić kilka czynności długo działa i widziałem go w NewRelic. Atrybuty te rozwiązały problem i dały możliwość obsługi działań równoległych dla tego kontrolera.

0

dla każdego czytającego to doświadczanie użycia IIS wysokiego CPU, które jest niewyjaśnione, spójrz na ten wątek z forum pomocy NewRelic, które otworzyłem.

Zajmuję się tym problemem przez ponad tydzień, dopóki nie zorientowałem się, że to ich agent był przyczyną problemu.

.NET agent causing high CPU usage on IIS

więc pierwszą rzeczą, którą proponuję spróbować jest usunięcie.Agent NET i sprawdź, czy są jakieś zmiany, zanim zanurkujesz w bardziej złożone eksperymenty.

Zamieszczam tę odpowiedź w tym konkretnym pytaniu, ponieważ dostałem się tutaj pierwszy podczas pracy nad sprawą, a zwinność wątku ustawiła mnie na długim kursie, który nie był poprawny ... (chociaż bardzo interesujący sam w sobie).

2

Napotkałem ten sam problem w aplikacji, która miała bardzo minimalne wykorzystanie sesji. Po uwzględnieniu zaakceptowanej odpowiedzi, a nawet tymczasowym usunięciu SessionState do celów diagnostycznych, nadal miałem znaczące problemy z wydajnością w tej "Metodzie" Na podstawie komentarza todd_saylor w this thread wykonałem wiele badań samodzielnie strony i okazało się, że BeginProcessRequest może służyć jako catch-all New Relic dla różnych strat wydajności, które pojawiają się w różnych obszarach kodu. Byłem w stanie znacznie skrócić czas w tym obszarze, profilując kod na mojej lokalnej maszynie za pomocą ANTs Performance Profiler Red Gate.

Mój lokalny profilowanie ujawnił kilka rzeczy:

  1. używałem Ninject jako mojego kontenera DI, który spowodował utratę wydajności w swojej rezolucji Object. Wymieniłem go na SimpleInjector i zwiększono wydajność o rząd wielkości.
  2. Znalazłem, że gdzieś pomiędzy AutoMapper's IQueryable Extensions Project().To<>() a dostawcą Linq's Sql Server, że Dynamic Compiling i JITing projekcji był odpowiedzialny za 50% czasu mojej prośby. Zastąpienie tego przez CompiledQuerys spowodowało kolejny znaczący spadek.

Mam nadzieję, że to pomoże komuś innemu!

Powiązane problemy