Что происходит в BeginProcessRequest()?


мы используем NewRelic для обеспечения трассировки приложений на стороне сервера.

мы заметили, что некоторые из наших приложений постоянно тратят около 100 мс в методе System.Web.Mvc.MvcHandler.BeginProcessRequest().

это происходит до вызова любого пользовательского кода контроллера (который регистрируется отдельно, а не кумулятивно) - не очевидно, почему он будет тратить так много времени в этом методе.

какие вещи MVC будет делать в этом методе? Может это просто быть запрос в очереди?

[EDIT:]как и предполагалось-ответ скаляра ниже был точен. Мы удалили и оптимизировали все наши зависимости от сеанса и увидели массовое увеличение масштабируемости и стабильности приложений

4 56

4 ответа:

то, что вы могли бы видеть, обычно называют ловкостью потока в .NET.

то, что вы, вероятно, видите, насколько результаты под актуальной меткой (т. е. код приложения в System.Web.HttpApplication.BeginRequest()) - это проблема гибкости потока; в большинстве случаев время, которое вы видите здесь, не обязательно выполняется код, но веб-контекст ожидает, что потоки будут выпущены обратно в него из блокировки чтения-записи.

The Application_BeginRequest() "пауза" - это тот, который довольно распространен в a ASP.NET веб-стек. В общем, когда вы видите длительное время загрузки в BeginRequest, вы имеете дело с ASP.NET гибкость потоков и / или блокировки потоков - особенно при работе с операциями ввода-вывода и сеансами. Не то, чтобы это плохо, это просто, как .net гарантирует, что ваши потоки остаются параллельными.

промежуток времени обычно происходит между BeginRequest и PreRequestHandlerExecute. Если приложение пишет несколько вещей для сеанса, то ASP.NET выдаст читателю-писателю замок на HttpContext.Current.Session.

хороший способ узнать, является ли это проблемой, с которой вы можете столкнуться, - это проверить идентификаторы потоков, чтобы узнать, является ли проблема гибкостью-идентификаторы будут отличаться для данного запроса.

например. Во время отладки, возможно, вы могли бы добавить следующий код Global.asax.cs:

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

откройте окно вывода отладки (из Visual Studio: View >> Output, затем выберите "Debug" из раскрывающегося списка "show output from").

во время отладки, нажмите страница, где вы видели долгое время. Затем просмотрите журнал вывода - Если вы видите несколько идентификаторов, то вы можете страдать от этого.

вот почему вы можете видеть задержку иногда, но не в других случаях, код приложения может использовать сеанс немного по-другому или сеанс или операции ввода-вывода могут быть выше или ниже от страницы к странице.

если это так, некоторые вещи, которые вы можете сделать, чтобы помочь ускорить работу в зависимости от того, как сеанс используется на сайте или каждый данный страница.

для страниц, которые не изменяют сессии:

   <% @Page EnableSessionState="ReadOnly" %>

для страниц, которые не используют состояние сеанса:

<% @Page EnableSessionState="False" %>

если приложение не использует сеанс (web.конфигурации):

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

Итак, давайте возьмем следующий пример:

пользователь загружает страницу, а затем решает перейти на другую страницу, прежде чем первый запрос будет выполнен загрузка ASP.NET вызовет блокировку сеанса, в результате чего загрузка нового запроса страницы будет ждать завершения первого запроса страницы. С помощью ASP.NET MVC каждое действие блокирует сеанс пользователя для синхронизации; вызывая ту же проблему.

все время, которое потребовалось для разблокировки блокировки, будет сообщено через new relic, не говоря уже о тех, где пользователь отказался от сеанса, и поток, возвращающийся, ищет пользователя, который больше не существует.

кстати элемент управления UpdatePanel вызывает такое же поведение -

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

что можно сделать:

эта проблема блокировки является одной из причин Microsoft имеет класс SessionStateUtility -

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

так что вы можете переопределить поведение по умолчанию, если вы столкнулись с этой проблемой, как показано здесь в этом Redis реализация:https://github.com/angieslist/AL-Redis

существует много вариантов поставщика состояния по умолчанию, используемого веб-сайтами на основе .net. Но знайте, что обычно это время транзакции указывает на то, что потоки блокируются и ждут запросов к серверу, которые будут завершены.

Если вы хотите включить определенный контроллер для параллельной обработки запросов от одного пользователя, вы можете использовать атрибут с именем SessionState, который был введен в MVC начиная с версии 3. Нет необходимости использовать контроллер без сеанса для достижения параллелизма, опция Ready-only SessionStateBehavior позволит вам пройти проверки безопасности, которые вы, возможно, реализовали на основе данных сеанса.

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

у меня также были задержки в System.Web.Mvc.MvcHandler.BeginProcessRequest(), когда пытаются сделать несколько длинных запуск действия, и я видел его в NewRelic. Эти атрибуты решили проблему и дали возможность обрабатывать действия параллельно для этого контроллера.

я столкнулся с этой же проблемой в приложении, которое имело очень минимальное использование сессий. После учета принятого ответа и даже временного удаления SessionState полностью для диагностических целей у меня все еще были значительные проблемы с производительностью в этом "методе"

на основе комментария todd_saylor в этой теме, Я сделал много исследований на свой сайт и обнаружил, что BeginProcessRequest смогите быть подачей как улов новой реликвии-все для разностороннего представления потери, которые появляются в разных областях кода. Я смог значительно сократить время в этой области, профилируя код на моей локальной машине с помощью профилировщика производительности Муравьев Red Gate.

мое местное профилирование показало несколько вещей:

  1. Я использовал Ninject в качестве контейнера DI, что вызвало потерю производительности в его разрешении объекта. Я заменил его SimpleInjector и увеличил производительность на порядок.
  2. Я нашел это где-то между расширениями AutoMapper IQueryable Project().To<>() и аттракцион LINQ в SQL-сервер провайдера, что динамическая компиляция и преобразование в формат JIT проекций отвечает за 50% времени запроса. Заменив его на CompiledQuerys сделал еще одну значительную вмятину.

надеюсь, это поможет кому-то еще!

для тех, кто читает это, испытывая высокую загрузку ЦП IIS, которая необъяснима, взгляните на эту тему с форума поддержки NewRelic, который я открыл.

Я занимаюсь этой проблемой уже более недели, пока не понял, что именно их агент был основной причиной проблемы.

.Чистая агентов, вызывая высокую загрузку процессора на сервере IIS

поэтому первое, что я предлагаю вам попробовать, это удалить агент .NET и посмотреть, есть ли какие-либо изменения, прежде чем погружаться в более сложные эксперименты.

Я публикую этот ответ на этот конкретный вопрос, так как я попал сюда первым, работая над проблемой, и гибкость потока установила меня на длинный курс, который был неправильным...(хотя очень интересно само по себе).