Увеличение активности обмена данными между микросервисами часто является проблемой в архитектуре современных ИТ-решений.
Извлечь из этого максимум пользы и выжить любой ценой – серьезная задача для любого развития.
Поэтому поиск оптимальных решений является непрерывным процессом.
В статье кратко изложены проблемы, которые могут возникнуть при использовании высоконагруженных http-запросов и способы их обхода.
Эта история начинается с ошибки.
Однажды мы проводили нагрузочное тестирование, основным элементом которого было выполнение большого количества коротких http-запросов.
Клиент написан под неткор 2.2 , начиная с какого-то момента, выдается System.Net.Sockets.SocketException: адрес уже используется .
Быстро выяснилось, что порты на клиенте не успели освободиться, и в какой-то момент система отказалась открывать новый.
Теперь, если мы перейдем к коду, проблема заключалась в использовании старого подхода с классом HttpWebRequest и конструкции:
Казалось бы, мы освобождаем ресурс, и порт должен освобождаться своевременно.var request = WebRequest.CreateHttp(uri); using(var resp = request.GetResponse()){ … }
Однако нетстат сигнализировал о быстром увеличении количества портов в состоянии TIME_WAIT. Это состояние означает ожидание закрытия соединения (и, возможно, получение потерянных данных).
В результате порт может оставаться в нем 1-2 минуты.
Эта проблема довольно подробно обсуждается во многих статьях ( Проблемы с очередью TIME_WAIT , История о TIME_WAIT ).
Однако это означает, что дотнет «честно» пытается закрыть соединение, а то, что происходит дальше, происходит по вине настроек таймаута в системе.
Почему это происходит и как с этим бороться
я не буду говорить о поддерживать жизнь .Об этом вы можете прочитать сами.
Цель статьи — попытаться обойти грабли, тщательно расставленные на пути разработчика.
В соответствии с MSDN , свойство KeepAlive сорт HttpWebRequest по умолчанию равно истинный .
То есть все это время HttpWebRequest «обманул» сервер, попросив его сохранить соединение, после чего сам его отключил.
Если быть более точным, HttpWebRequest при настройках по умолчанию он не отправлял заголовок «Соединение: Keep-Alive», просто этот режим подразумевается в стандарте HTTP/1.1 .
Первое, что вам следует попробовать, — это принудительно отключить KeepAlive. Если вы установите HttpWebRequest.KeepAlive = false, то в запросе появится заголовок «Соединение: закрыть».
Надо признать, что на испытательном стенде это полностью решило проблему.
В качестве сервера был настроен Nginx со статической страницей.
Был протестирован следующий код: while (true)
{
var request = WebRequest.CreateHttp(uri);
request.KeepAlive = false;
var resp = await request.GetResponseAsync();
using (var sr = new StreamReader(resp.GetResponseStream()))
{
var content = sr.ReadToEnd();
}
}
Однако при попытке запуска на серверном оборудовании при больших нагрузках (более 1000 запросов в секунду) этот код снова начал выдавать те же ошибки.
Только вот порты были в состоянии CLOSE_WAIT, LAST_ACK. Это предфинальные состояния закрытия соединения, когда клиент ожидает подтверждения от инициатора закрытия.
Такое поведение сигнализирует о том, что клиент начинает перегружаться новыми открытыми соединениями.
Невозможно закрыть, использовать повторно.
Действительно, для достижения максимальной производительности соединение необходимо использовать повторно.
Для этого необходимо включить режим поддерживать жизнь и пойти на урок HttpClient .
Как именно это работает и как лучше этим пользоваться стоит прочитать Здесь И Здесь .
Другой вопрос, как сделать так, чтобы соединения использовались повторно? Существование одного Keep-Alive соединения регулируется двумя основными параметрами на сервере nginx:
- Keepalive_timeout – время жизни (в среднем 15 секунд)
- Keepalive_requests — максимальное количество запросов в одном соединении (по умолчанию 100)
Только разоблачив Keepalive_requests при более высоких значениях (> 1000) видно, что все работает как надо.
Заключение
Если вы не используй http запросы в режиме высокой нагрузки, то вам подойдет любой вариант. Вряд ли вы успеете исчерпать все порты.Если в вашем приложении нет смысла повторно использовать соединения, например, вы редко обращаетесь к серверу повторно, то вам следует сознательно отключить поддерживать жизнь .
Также поддерживать жизнь Его следует использовать правильно и осторожно при большом потоке запросов, корректируя время жизни соединения в зависимости от частоты повторных обращений к серверу.
И, наконец, немного сравнения производительности тестов:
- RunHttpClient – использует класс HttpClient в режиме «Соединение: поддержание активности».
- RunHttpClientClosed — использует класс HttpClient в режиме «Соединение: закрыто».
- RunWebRequestClosed — использует класс HttpWebRequest в режиме «Соединение: закрыто».
- Keepalive_timeout 60 с;
- Keepalive_requests 100000;
Метод | Н | Головы | Иметь в виду |
---|---|---|---|
ЗапуститьHttpClient | 1000 | 1 | 963,3 мс |
ЗапуститьWebRequestClosed | 1000 | 1 | 3857,4 мс |
ВыполнитьHttpClientClosed | 1000 | 1 | 1612,4 мс |
ЗапуститьHttpClient | 10000 | 1 | 9573,9 мс |
ЗапуститьWebRequestClosed | 10000 | 1 | 37 947,4 мс |
ЗапуститьHttpClientClosed | 10000 | 1 | 16112,9 мс |
NET #высоконагруженные проекты #http #разработка .
net #keep-alive
-
Руководство По Устранению Неполадок Печати
19 Oct, 24 -
Каскадер — Зачем Аниматорам Физика?
19 Oct, 24 -
Примечание О Мастерстве
19 Oct, 24 -
Как Звуки Влияют На Наш Сон И Продуктивность
19 Oct, 24 -
Ebay Продал Skype
19 Oct, 24