Как видно из заголовка, у меня есть очень необычная ситуация, которую я просто не могу объяснить, поэтому в качестве последнего средства я публикую ее здесь.
Лучше всего проиллюстрировать ее с помощью кода сразу.
У меня есть следующая ситуация где-то в моей реализации IHttpHandler
:
var requestData = /*prepare some request data for result service...*/;
//Call the service and block while waiting for the result
MemoryStream result = libraryClient.FetchResultFromService(requestData).Result;
//write the data back in the form of the arraybuffer
HttpContext.Current.Response.BinaryWrite(CreateBinaryResponse(result));
FetchResultFromService
- это метод асинхронной библиотеки, который использует HttpClient
для извлечения результата из удаленной службы.Упрощенная версия выглядит следующим образом:
public async Task<MemoryStream> FetchResultFromService<T>(T request)
{
...//resolve url, formatter and media type
//fire the request using HttpClient
HttpResponseMessage response = await this.client.PostAsync(
url,
request,
formatter,
mediaType);
if (!response.IsSuccessStatusCode)
{
throw new Exception("Exception occurred!");
}
//return as memory stream
var result = new MemoryStream();
await response.Content.CopyToAsync(result);
result.Position = 0;
return result;
}
Проблема возникает, когда для ответа FetchResultFromService
требуется более 2 минут.В этом случае IIS прерывает заблокированный поток (для таймаута IIS установлено значение 120 секунд) с ThreadAbortException .Клиент (браузер) получает ответ об ошибке, и на данный момент все выглядит нормально, но вскоре после этого производительность приложения падает, а время отклика стремительно растет.
Глядя на журналы «Библиотечного сервиса», становится ясно, что момент, когда сервис окончательно завершен (2+ минуты) и возвращает ответ, - это момент, когда происходит аномалия на веб-сервере.Проблема обычно решается сама через одну или две минуты после начала.
Надеюсь, теперь название стало понятнее. При продолжении выполняемой задачи ПОСЛЕ того, как поток вызывающего абонента завершил запрос, страдает все приложение.
Если я изменяю код библиотеки с помощью ConfigureAwait(false)
, эта проблема не возникаетt происходит.
HttpResponseMessage response = await this.client.PostAsync(
url,
request,
formatter,
mediaType).ConfigureAwait(false);//this fixes the issue
Так что теперь похоже, что это связано с синхронизацией контекста.Приложение использует старый LegacyAspNetSynchronizationContext , который блокирует объекты HttpAplication
, но я не могу понять, как это может повлиять на все потоки / запросы.
Теперь я понимаю, что их многоплохих практик проиллюстрировано выше.Мне известны такие статьи, как Не блокировать в асинхронном коде , и, хотя я благодарен за все полученные ответы, я в большей степени ищу объяснение того, как один единственный асинхронный запрос может привести целое приложение к егоколени в этой ситуации.
Дополнительная информация
- Мы не говорим о тупиках из-за блокировки.Вышеуказанная ситуация выполняется без проблем, если службе удается вернуть результат в те 2 минуты, которые IIS позволяет ему сделать. Проблема может быть воспроизведена, даже если вы не блокируете и просто вызываете метод библиотеки, не вызывая
Result
getter . - Удаление секундного ожидания (копирование
MemoryStream
) уменьшает воспроизведениеОцените это много, но это все же возможно при одновременном выполнении нескольких вызовов Library FetchResultFromService
.ConfigureAwait(false)
в настоящее время является единственным надежным «решением». - Я даже обнаружил, что IIS даже явно разрывает соединения ACTIVE.Я использовал JMeter для стресс-тестирования приложения, и когда происходит аномалия, потоки JMeter (фальшивые пользователи) сообщают, что соединение было сброшено.При ручном тестировании с Chrome я также заметил это падение с ответом
ERR_CONNECTION_RESET
.Эти сбросы происходят регулярно, когда приложение находится под нагрузкой и возникает аномалия, но, как уже упоминалось выше, аномалия всегда проявляется как снижение производительности для большинства активных пользователей. - Приложение представляет собой приложение WebForms, которое реализует и использует пользовательскийсинхронный
IHttpHandler
для большинства своих запросов.Реализация обработчика объявлена как IRequiresSessionState , обеспечивающая эксклюзивный доступ к сеансу.Я упоминаю об этом, так как у меня есть догадка, что это может быть связано с состояниями сеанса, хотя я не могу это подтвердить.Или, может быть, это связано с вызовом асинхронных методов из обработчиков синхронизации, хотя все равно хотелось бы знать, как. - Версия IIS 7, и журналы IIS не показывают ничего полезного (или вовсе).
У меня нет идей для тестирования, надеюсь, кто-то хотя бы подскажет мне в каком-то направлении.Я бы очень хотел это понять.Спасибо!