Шок! Четыре онлайн-машины со всеми OOM одновременно, что случилось?

Java

Место преступления

Прошлой ночью я внезапно получил большое количество предупреждений от APM (сокращение от Application Performance Management, механизма онлайн-мониторинга и раннего предупреждения о производительности и надежности приложений, который мы создали внутри с помощью SMS).

(Голос за кадром: Мониторинг — очень важное средство обнаружения проблем, если нет, то его нужно вовремя установить)

Сразу после операции и обслуживания позвонили, чтобы сообщить, что все четыре машины, развернутые в сети, были OOM (недостаточно памяти, нехватка памяти), все службы были недоступны, и быстро проверить проблему!

Устранение неполадок

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

В первый раз я подумал о сбросе состояния памяти в то время, но поскольку эксплуатация и обслуживание перезапустили машину, чтобы как можно скорее восстановить службу в сети, память на момент аварии не могла быть сброшена. Поэтому я еще раз взглянул на диаграмму мониторинга JVM в нашем APM.

Голос за кадром: Один способ не работает, попробуйте другой угол! Опять же, мониторинг очень важен! Идеальный мониторинг может восстановить место происшествия в то время, что удобно для обнаружения проблемы.

Я не знаю, если я этого не вижу, но я был поражен, когда увидел.Треды, созданные в приложении, начали подниматься каждый момент с 16:00 до примерно 3 нед.После перезапуска (синяя стрелка) потоки растут) , Какое количество потоков при нормальных условиях, 600! Проблема найдена.Должно быть проблемный код был отправлен около 16:00 дня, из-за чего тред постоянно создавался, а созданный тред не умер!Проверил запись релиза и обнаружил что запись релиза имеет только такой подозрительный код diff: in Дополнительный добавляется при инициализации HttpClientevictExpiredConnectionsнастроить

Проблема локализована, она должна быть вызвана этой конфигурацией! (Момент времени подъема потока точно совпадает с моментом времени выпуска!), поэтому вновь добавленная конфигурация была сначала удалена, а количество потоков вернулось к нормальному после выхода в сеть. ЭтоevictExpiredConnectionsЧто вы сделали, чтобы количество потоков постоянно увеличивалось? Для решения какой проблемы добавлена ​​эта конфигурация? Поэтому я нашел соответствующих коллег, чтобы понять причины и последствия добавления этой конфигурации.

Восстановление того, что произошло

В последнее время в сети появилось многоNoHttpResponseExceptionисключение, что вызывает это исключение?

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

Давайте сначала посмотрим на жизненный цикл обычного TCP-соединения.

Вы можете видеть, что каждое TCP-соединение проходит черезтрехстороннее рукопожатиеДанные могут быть отправлены только после установления соединения.помахал четыре разаСоединение может быть разорвано.Если каждое TCP-соединение будет разорвано сразу после того, как сервер вернет ответ, несколько HTTP-запросов будут созданы и разъединены несколько раз.Много HTTP-запросовВ данном случае это, несомненно, очень требовательно к производительности.Если сервер возвращает ответ, TCP-соединение не сразу разрывается, амультиплексЭта ссылка делает следующий Http-запрос, который практически исключает много накладных расходов на создание/отключение TCP, и производительность, несомненно, будет значительно улучшена.

Как показано на рисунке ниже, левое изображение представляет собой случай нескольких HTTP-запросов без мультиплексирования TCP, а правое изображение — случай мультиплексирования TCP. Вы можете видеть, что инициируются три HTTP-запроса. Если вы повторно используете TCP, вы можете сэкономить необходимость устанавливать/отключать TCP дважды.Теоретически приложению нужно только открыть TCP-соединение, и другие HTTP-запросы могут повторно использовать это TCP-соединение, так что n HTTP-запросов могут сэкономить в n-1 раз накладные расходы на создание/отключение TCP . Это, несомненно, огромная помощь в повышении производительности.

Оглядываясь назад, поддержание активности (также известное как постоянное соединение, мультиплексирование соединения) заключается в повторном использовании соединений, чтобы гарантировать, что соединение является постоянным и эффективным.

(Закадровый голос: поддержка активности поддерживается и включается по умолчанию после HTTP 1.1, но большинство веб-сайтов в настоящее время используют http 1.1, что означает, что большинство из них по умолчанию поддерживают повторное использование ссылок)

бесплатного обеда не бывает,Хотя keep-alive избавляет от многих ненужных операций рукопожатия/махания, но так как соединение поддерживается в течение длительного времени, при отсутствии http-запроса соединение будет простаивать долгое время, что будет занимать системные ресурсы, и иногда это будет больше, чем Мультиплексирование соединений приводит к большему потреблению производительности. Поэтому мы обычно устанавливаем тайм-аут для поддержания активности, чтобы, если соединение было бездействующим в течение установленного периода тайм-аута (передачи данных не было), соединение было разорвано по истечении периода тайм-аута, что может снизить нагрузку на систему.

Кажется, что добавление таймаута в keep-alive — это прекрасно, но это вносит новые проблемы (одна волна сгладилась, другая поднялась!), рассмотрим следующую ситуацию:

Если сервер закрывает соединение и отправляет пакет FIN (Примечание: если сервер не получил запрос клиента в течение установленного времени ожидания, сервер будет активно инициировать запрос с флагом Fin для отключения и освобождения ресурсов), в этом FIN В течение периода, когда пакет отправлен, но еще не достиг клиента, если клиент продолжает мультиплексировать TCP-соединение для отправки сообщения HTTP-запроса, сервер отправит клиенту сообщение RST, поскольку он не получил сообщение во время четыре волны.При получении сообщения RST будет предложено исключение (т.е.NoHttpResponseException)

Давайте воспользуемся блок-схемой, чтобы тщательно разобраться в приведенном выше поколении.NoHttpResponseException, чтобы было видно четче

После стольких усилий мы, наконец, знаем, что производит **NoHttpResponseException**, как решить, есть две стратегии

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

evictExpiredConnectionsЭто вторая стратегия, используемая выше. Давайте посмотрим на официальные инструкции по использованию.

Makes this instance of HttpClient proactively evict idle connections from the
connection pool using a background thread.

Вызов этого метода будет генерировать только поток синхронизации, который всегда был увеличен в приложении, потому что мы создали HTTPCLIENT для каждого запроса!Это вызывается, потому что каждый экземпляр httpclient вызываетсяevictExpiredConnections, в результате чего будет создано сколько потоков по времени для скольких запросов!

Есть еще вопрос, почему все четыре машины на линии висят практически одновременно? Поскольку из-за балансировки нагрузки вес этих четырех машин одинаков, и аппаратная конфигурация тоже одинакова, фактически полученные запросы можно считать похожими, так что фоновые потоки, генерируемые этими четырьмя машинами из-за создание HttpClient также достигает того же времени.Высшая точка, затем OOM в то же время.

Решать проблему

Поэтому, в ответ на проблемы, упомянутые выше, мы сначала изменили HttpClient на singleton, чтобы гарантировать, что после запуска службы будет только один регулярный поток очистки. потоков приложения.Пороговое значение сообщается напрямую, чтобы его можно было обнаружить и обработать до того, как будет применено OOM. Голос за кадром: Опять же, мониторинг очень важен, он может убить проблему в пеленках!

Суммировать

В этой статье подробно анализируется причина проблемы через явление одновременного OOM четырех машин онлайн.. Видно, что когда мы применяем библиотеку, мы должны сначала иметь полное представление о библиотеке (вышеупомянутый HttpClient создается без Singleton это явно проблема), а во-вторых, необходимые сетевые знания все же нужны, поэтому, чтобы стать квалифицированным программистом, речь идет не о знании самого языка, но и сети, базы данных и т. д. Проблемы и настройка производительности будут очень кстати .Опять же, очень важен безупречный мониторинг.Включив определенный порог оповещения заранее, можно убить проблему в кредле!