Подтвердить что ты не робот

Хранение всего в сеансе ASP.NET вызывает задержки 500 мс

Проблема:

Когда вы используете сеанс на сайте ASP.NET, он вызывает значительные задержки (кратные 500 мс) при загрузке нескольких запросов почти в одно и то же время.

Более конкретно, Моя проблема

Наш сайт использует сессию исключительно для своего SessionId. Мы используем этот ключ для поиска таблицы db с информацией о пользователе и т.д. Это казалось хорошим дизайном, поскольку оно хранило минимальные данные сеанса. К сожалению, SessionId изменится, если вы ничего не храните в сеансе, поэтому мы сохраняем Session["KeepId"] = 1;. Этого достаточно, чтобы убедить SessionId не меняться.

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

По какой-либо причине, когда вы (а) отправляете несколько запросов одновременно и (б), задействуете какой-либо сеанс, тогда вы получите случайные задержки 500 мс примерно на 1/2 времени. В некоторых случаях эти задержки будут составлять 1000 мс или более, всегда увеличиваясь с интервалом ~ 500 мс. Больше запросов означает более длительное время ожидания. На нашей странице с десятками изображений можно подождать 10+ секунд для некоторых изображений.

Как воспроизвести проблему:

  • Создание пустого веб-приложения ASP.NET MVC
  • Создайте действие пустого контроллера:

    public class HomeController : Controller
    {
      public ActionResult Test()
      {
        return new EmptyResult();
      }
    }
    
  • Создайте страницу test.html с несколькими тегами img, которые попали в это действие:

    <img src="Home/Test?1" />
    <img src="Home/Test?2" />
    <img src="Home/Test?3" />
    <img src="Home/Test?4" />
    
  • Запустите страницу и посмотрите время быстрой загрузки в firebug:

    Each image loads reasonably fast

  • Сделайте одно из следующих действий (оба имеют одинаковый результат):

    • Добавить пустой обработчик Session_Start в ваш файл Global.asax.cs

      public void Session_Start() { }
      
    • Поместите что-нибудь в сеанс

      public class HomeController : Controller
      {
        public ActionResult Test()
        {
          HttpContext.Current.Session["Test"] = 1;
          return new EmptyResult();
        }
      }
      
  • Запустите страницу еще раз и обратите внимание на случайные/случайные задержки в ответах.

    Some requests experience long delays

Что я знаю до сих пор

Мой вопрос

Как я могу использовать сеанс, но избегаю этих задержек? Кто-нибудь знает об исправлении?

Если нет исправления, я думаю, нам придется обойти сеанс и использовать файлы cookie напрямую (сеанс использует файлы cookie).

4b9b3361

Ответ 1

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

Досадная часть - если я запустил все 5 запросов в последовательном порядке (из примера), это займет ~ 40 мс. С ASP.NET блокировка его более 1000 мс. Похоже, что ASP.NET говорит: "Если сеанс используется, тогда спящий на 500 мс и повторите попытку".

Если вы используете StateServer или SqlServer вместо InProc, это не поможет - ASP.NET по-прежнему блокирует сеанс.

Есть несколько разных исправлений. Мы закончили тем, что использовали первый.

Вместо этого используйте Cookies

Cookies отправляются в заголовке каждого запроса, поэтому вы должны держать его в тени и избегать конфиденциальной информации. При этом сеанс использует файлы cookie по умолчанию, чтобы запомнить, кто есть кто, сохраняя строку ASPNET_SessionId. Все, что мне нужно, это идентификатор, поэтому нет причин переносить блокировку сеанса ASP.NET, когда это всего лишь оболочка вокруг идентификатора в файле cookie.

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

Использовать атрибуты MVC

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

Для приложений MVC 3 используйте этот атрибут на вашем контроллере, чтобы сделать сеанс доступным только для чтения (он не работает над конкретным действием):

[SessionState(SessionStateBehavior.ReadOnly)]

Отключить сеанс для некоторых маршрутов

Вы также можете отключить сеанс с помощью маршрутизации MVC, но это немного сложно.

Отключить сеанс на определенной странице

Для WebForms вы можете отключить сеанс для определенных страниц aspx.

Ответ 2

Я взглянул на структуру ASP.NET. Здесь определяется класс, в котором управляется состояние сеанса: http://referencesource.microsoft.com/#System.Web/State/SessionStateModule.cs,114

Вот как это работает (упрощенный код):

LOCKED_ITEM_POLLING_INTERVAL = 500ms;
LOCKED_ITEM_POLLING_DELTA = 250ms;

bool GetSessionStateItem() {
    item = sessionStore.GetItem(out locked);

    if (item == null && locked) {
        PollLockedSession();
        return false;
    }
    return true;
}

void PollLockedSession() {
    if (timer == null) {
        timer = CreateTimer(PollLockedSessionCallback, LOCKED_ITEM_POLLING_INTERVAL);
    }
}

void PollLockedSessionCallback() {
    if (DateTime.UtcNow - lastPollCompleted >= LOCKED_ITEM_POLLING_DELTA) {             
          isCompleted = GetSessionStateItem();
          lastPollCompleted = DateTime.UtcNow;

          if(isCompleted) {
              ResetPollTimer();
          }
    } 
}

Сводка: если элемент сеанса не может быть восстановлен, поскольку он заблокирован другим потоком, будет создан таймер. Он будет регулярно объединять сеанс, чтобы попытаться извлечь элемент снова (каждые 500 мс по умолчанию). Как только элемент был успешно восстановлен, таймер очищается. Кроме того, есть проверка, чтобы убедиться, что существует некоторая задержка между вызовами GetSessionStateItem() (LOCKED_ITEM_POLLING_DELTA= 250 мс по умолчанию).


Можно изменить значение по умолчанию LOCKED_ITEM_POLLING_INTERVAL, создав в реестре следующий ключ (это повлияет на все веб-сайты, запущенные на компьютере):

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET
SessionStateLockedItemPollInterval (this is a DWORD)

Другим способом (который является взломом) является изменение значения путем отражения:

Type type = typeof(SessionStateModule);
FieldInfo fieldInfo = type.GetField("LOCKED_ITEM_POLLING_INTERVAL",
   BindingFlags.NonPublic | BindingFlags.Static);
fieldInfo.SetValue(null, 100); //100ms

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: точные последствия снижения этого значения неизвестны. Это может увеличить конфликт потоков на сервере, создать потенциальные блокировки и т.д. Лучшее решение - избежать использования состояния сеанса или украсить ваш контроллер атрибутом SessionStateBehavior.ReadOnly, как предполагали другие пользователи.

Ответ 3

Существует несколько способов ускорить сеанс в ASP.NET. Прежде всего, несколько ключевых моментов:

  • Сессия является потокобезопасной, что означает, что она не играет хорошо с одновременными клиентскими запросами, которым необходимо получить к ней доступ. Если вам нужны асинхронные процессы для доступа к данным типа сеанса, вы можете использовать некоторую другую временную память, такую ​​как кеш или вашу базу данных (для отслеживания прогресса async это необходимо). В противном случае информация о сеансе пользователей будет блокироваться в контексте запроса, чтобы предотвратить проблемы с его изменением. Следовательно, почему запрос 1 в порядке, тогда следующие запросы имеют задержку. PS это абсолютно необходимо для обеспечения целостности данных в сеансе... например, пользовательских заказов в электронной торговле.

  • Сеанс сериализуется и выводится на сервер. Файл cookie используется для поддержки сеанса, хотя это другой тип файла cookie, что означает, что я не ожидал бы большой разницы в производительности.

Таким образом, мой любимый способ ускорить сеансовые запросы - всегда использовать государственный сервер. Он устраняет проблемы, связанные с процессом, а также означает, что при тестировании вы можете перестроить свой проект без повторного входа на свой сайт. Это также делает сеансы возможными в простых сценариях балансировки нагрузки. http://msdn.microsoft.com/en-us/library/ms972429.aspx

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

Ответ 4

Я просто хотел добавить небольшую деталь к большому ответу @bendytree. При использовании WebForms вы можете не только полностью отключить состояние сеанса

<%@ Page EnableSessionState="False" %>

но также установите его только для чтения:

<%@ Page EnableSessionState="ReadOnly" %>

Это решило описанную проблему в моем случае. См. документация