Хранение чего-либо в сеансе ASP.NET вызывает задержки 500 мс - PullRequest
32 голосов
/ 02 декабря 2011

Проблема:

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

Подробнее, Моя проблема

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

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

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

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

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

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

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

    Each image loads reasonably fast

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

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

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

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

    Some requests experience long delays

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

Мой вопрос

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

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

Ответы [ 4 ]

19 голосов
/ 02 декабря 2011

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

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

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

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

Используйте куки вместо

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

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

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

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

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

[SessionState(SessionStateBehavior.ReadOnly)]

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

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

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

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

9 голосов
/ 18 сентября 2015

Я взглянул на структуру 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, как предлагали другие пользователи.

2 голосов
/ 02 декабря 2011

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

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

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

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

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

1 голос
/ 11 июня 2015

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

<%@ Page EnableSessionState="False" %>

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

<%@ Page EnableSessionState="ReadOnly" %>

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

...