Хранить что-нибудь внутри ASP.NET сеанс вызывает задержки 500ms
Проблема:
при использовании сеанса в ASP.NET сайт, он вызывает резкие задержки (кратные 500 мс) при загрузке нескольких запросов почти одновременно.
Более Конкретно, Моя Проблема
наш веб-сайт использует сеанс исключительно для своего SessionId. Мы используем этот ключ для поиска таблицы БД с информацией о пользователе и т. д. Это казалось хорошим дизайном, так как он хранил минимальные данные сеанса. К сожалению, SessionId изменится, если вы сохраните ничего в сессии, поэтому мы храним Session["KeepId"] = 1;
. Этого достаточно для убедить SessionId не менять.
на, казалось бы, несвязанной ноте сайт обслуживает настроенные изображения продуктов через действие контроллера. Это действие при необходимости генерирует изображение, а затем перенаправляет его в кэшированное изображение. Это означает, что одна веб-страница может содержать изображения или другие ресурсы, отправляя десятки запросов через ASP.NET трубопровод.
по любой причине, когда вы (a) отправляете несколько запросы в то же время и (b) имеют сеанс, участвующий в любом случае, то вы в конечном итоге со случайными задержками 500ms около 1/2 времени. В некоторых случаях эти задержки будут 1000ms или больше, всегда увеличивая в интервалах ~500ms. Больше запросов означает больше времени ожидания. Наша страница с десятками изображений может ждать 10 + секунд для некоторых изображений.
как воспроизвести проблему:
- создать пустой ASP.NET веб-приложение MVC
-
создать пустой действие контроллера:
public class HomeController : Controller { public ActionResult Test() { return new EmptyResult(); } }
-
сделать тест.html-страница с несколькими тегами img, которые попали в это действие:
<img src="Home/Test?1" /> <img src="Home/Test?2" /> <img src="Home/Test?3" /> <img src="Home/Test?4" />
-
запустите страницу и посмотрите время быстрой загрузки в firebug:
-
выполните одно из следующих действий (оба имеют одинаковый результат):
-
добавьте пустой обработчик Session_Start в глобальный.асакс.cs
public void Session_Start() { }
-
поставить что-то в сеанс
public class HomeController : Controller { public ActionResult Test() { HttpContext.Current.Session["Test"] = 1; return new EmptyResult(); } }
-
-
запустите страницу еще раз и обратите внимание на случайные/случайные задержки в ответах.
Что Я Знаю До Сих Пор
- это происходит с MVC 2 & 3 и WebForms
- это происходит с любым браузером (мы пробовали FF, Chrome, IE, Safari)
- случается независимо от того, подключен ли отладчик. Кроме того, ничего не должно быть на самом деле хранится в сеансе (см. Пример Session_Start выше).
- это происходит с WebDev.exe, IIS Express, & IIS
У Меня Вопрос
как я могу использовать сессии, но избежать этих задержек? Кто-нибудь знает исправить?
если нет исправления, я думаю, нам придется обойти сеанс и использовать cookies напрямую (сессия использует cookies).
4 ответов
как упоминал Гатс, проблема в том, что ASP.NET блокирует сеанс, поэтому каждый запрос для одного сеанса должен выполняться последовательно.
раздражающая часть-если я запустил все 5 запросов последовательно (из примера), это займет ~40 мс. С ASP.NET ' s блокировка это более 1000ms. Кажется, что ASP.NET говорит: "если сеанс используется, то спите 500 мс и повторите попытку."
Если вы используете StateServer или SqlServer вместо InProc, это не поможет - ASP.NET все еще блокирует сеанс.
есть несколько различных исправлений. В итоге мы выбрали первый.
Вместо Этого Используйте Cookies
Cookies отправляются в заголовке каждого запроса, поэтому вы должны держать его легким и избегать конфиденциальной информации. Тем не менее, сеанс использует куки по умолчанию, чтобы помнить, кто есть кто, сохраняя строку ASPNET_SessionId. Все, что мне нужно, это удостоверение личности, так что нет причин терпеть. ASP.NET блокировка сеанса, когда это просто обертка вокруг id в печенье.
поэтому мы полностью избегаем сеанса и вместо этого сохраняем guid в cookie. Cookies не блокируются, поэтому задержки исправлены.
использовать атрибуты MVC
вы можете использовать сеанс, но избегайте блокировок определенных запросов, делая сеанс доступным только для чтения.
для приложений MVC 3 Используйте этот атрибут на контроллере, чтобы сделать сеанс доступным только для чтения (он не работает на определенном действии):
[SessionState(SessionStateBehavior.ReadOnly)]
отключить сеанс для некоторых Маршруты
вы можете отключить сеанс через маршрутизацию MVC, но это немного сложнее.
отключить сеанс на определенной странице
для WebForms вы можете отключить сеанс для определенных страниц aspx.
Я взглянул на 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();
}
}
}
Summary: если элемент сеанса не может быть получен, потому что он заблокирован другим потоком, будет создан таймер. Он будет регулярно объединять сеанс, чтобы попытаться снова получить элемент (каждые 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
атрибут, как предложили другие пользователи.
есть несколько способов ускорить сеанс в ASP.NET - ... Прежде всего несколько ключевых моментов:
Session является потокобезопасным, что означает, что он не играет хорошо с параллельными запросами клиентов, которые должны получить к нему доступ. Если вам нужны асинхронные процессы для доступа к данным типа сеанса, вы можете использовать другую форму временного хранилища, например кэш или вашу базу данных (для отслеживания асинхронного прогресса это необходимо). В противном случае информация о сеансе пользователя будет заблокирована в контексте запрос на предотвращение проблем с его изменением. Следовательно, почему запрос 1 прекрасен, то следующие запросы имеют задержку. PS Это абсолютно необходимо, чтобы гарантировать целостность данных в сеансе... как заказы пользователей в электронной коммерции.
сеанс сериализуется на сервере. Cookie используется для поддержания сеанса, хотя это другой тип cookie, что означает, что я не ожидаю большой разницы в производительности.
Так что мой любимый способ ускорить запросы сеанса-всегда использовать state server. Это устраняет проблемы в процессе, а также означает, что при тестировании вы можете перестроить свой проект без необходимости повторного входа на свой сайт. Это также делает сеансы возможными в простых сценариях балансировки нагрузки. http://msdn.microsoft.com/en-us/library/ms972429.aspx
PS Я должен добавить, что технически вне процесса Государственная служба должна быть медленнее. По моему опыту это быстрее и легче развиваться, но я бы предположил, что это зависит от того, как он используется.
Я просто хотел добавить немного деталей к большому ответу @bendytree. При использовании WebForms вы не можете полностью отключить состояние сеанса
<%@ Page EnableSessionState="False" %>
но также установите его в readonly:
<%@ Page EnableSessionState="ReadOnly" %>
это решило описанную проблему в моем случае. См.документация