Рейк На Пути К Поддержанию Жизни

Увеличение активности обмена данными между микросервисами часто является проблемой в архитектуре современных ИТ-решений.

Извлечь из этого максимум пользы и выжить любой ценой – серьезная задача для любого развития.

Поэтому поиск оптимальных решений является непрерывным процессом.

В статье кратко изложены проблемы, которые могут возникнуть при использовании высоконагруженных 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)
Если вы просматриваете соединения в нетстат или WireShark , то при больших нагрузках открытые порты на клиенте также будут быстро меняться.

Только разоблачив Keepalive_requests при более высоких значениях (> 1000) видно, что все работает как надо.



Заключение

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

Если в вашем приложении нет смысла повторно использовать соединения, например, вы редко обращаетесь к серверу повторно, то вам следует сознательно отключить поддерживать жизнь .

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

И, наконец, немного сравнения производительности тестов:

  • RunHttpClient – использует класс HttpClient в режиме «Соединение: поддержание активности».

  • RunHttpClientClosed — использует класс HttpClient в режиме «Соединение: закрыто».

  • RunWebRequestClosed — использует класс HttpWebRequest в режиме «Соединение: закрыто».

Сервер nginx настроен со следующими параметрами:
  • 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 мс
Теги: #Nginx #.

NET #высоконагруженные проекты #http #разработка .

net #keep-alive

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.