Крупная авария! Проблемы с вводом-выводом вызвали одновременный сбой 20 машин на линии.

Java

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

Исправление проблем

Аварийные сообщения: «Слишком много потоков, превышение порога», «Слишком низкая частота простоя ЦП». Когда я открыл систему мониторинга, все 20 сервисных узлов службы заказов вышли из строя, и служба не ответила.

Количество потоков каждого узла Springboot достигло максимального значения. Но память кучи JVM и GC не являются явно ненормальными. Уровень простоя ЦП в основном составляет 0%, но загрузка ЦП невелика, но время ожидания ввода-вывода очень велико. Ниже приведен снимок экрана с выполнением верхней команды для просмотра состояния ЦП:

На изображении выше мы видим:

Уровень простоя ЦП составляет 0% (идентификатор красного прямоугольника на рисунке выше)

Использование ЦП составляет 22% (красное поле на рисунке выше - это us 13% плюс sy 9%, us можно понимать как ЦП, занятый пользовательским процессом, а sy можно понимать как ЦП, занимаемый системным процессом)

Доля времени, которое ЦП тратит на ожидание операций дискового ввода-вывода, составляет 76,6 % (красная рамка wa на приведенном выше рисунке).

На данный момент можно определить, что проблема должна возникать в ожидании ввода-вывода. С помощью системы мониторинга и команды jstack наконец удалось установить, что проблема возникла при записи файла. Большое количество операций чтения и записи на диск приводит к исчерпанию ресурсов потока JVM (обратите внимание, что это не означает, что ЦП системы исчерпан). В конце концов, служба заказа не может ответить на запрос вышестоящей службы.

IO, вещи, которые вы не знаете

Поскольку ввод-вывод оказывает такое большое влияние на производительность и стабильность системы, давайте рассмотрим его более подробно.

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

Дисковый ввод-вывод: ввод и вывод диска, например, передача данных между диском и памятью.

Сетевой ввод-вывод: передача данных по сетям между различными системами, например вызовы удаленного интерфейса между двумя системами.

На следующем рисунке показан конкретный сценарий, в котором в приложении происходит ввод-вывод:

На приведенном выше рисунке мы можем понять конкретную сцену операции ввода-вывода. В процессе запроса может выполняться множество операций ввода-вывода:

  1. Сетевой ввод-вывод происходит, когда страница запрашивается на сервере

  2. Сетевой ввод-вывод будет происходить при удаленных вызовах между службами

  3. Сетевой ввод-вывод будет происходить, когда приложение обращается к базе данных.

  4. Дисковый ввод-вывод происходит при запросе или записи данных в базу данных

Отношения между вводом-выводом и процессором

Многие осадные львы понимают, что если показатель простоя ЦП равен 0%, это означает, что ЦП уже работает на полную мощность и у него нет энергии для решения других задач. Это действительно так?

Давайте сначала посмотрим, как компьютер управляет дисковыми операциями ввода-вывода. На заре развития компьютеров передача данных между диском и памятью контролировалась ЦП, то есть чтение данных с диска в память требовало ЦП для сохранения и пересылки, а ЦП всегда был занят во время этого. период. Мы знаем, что скорость чтения и записи диска намного меньше скорости процессора. Таким образом, во время передачи данных будет занято большое количество ресурсов ЦП, что приведет к серьезной трате ресурсов ЦП.

Позже кто-то разработал контроллер ввода-вывода для управления дисковым вводом-выводом. Перед передачей данных между диском и памятью ЦП отправит инструкцию контроллеру ввода-вывода, чтобы контроллер ввода-вывода отвечал за операцию передачи данных, а контроллер ввода-вывода уведомил ЦП после передачи данных. Таким образом, процесс чтения данных с диска в память больше не требует участия ЦП, а ЦП может быть освобожден для других задач, что значительно улучшает загрузку ЦП. Этот IO-контроллер — «DMA», то есть прямой доступ к памяти, Direct Memory Access. Современные компьютеры в основном используют этот режим DMA для передачи данных.

Из приведенного выше содержания мы знаем, что когда данные ввода-вывода передаются, они не занимают ЦП. Когда процесс приложения или поток ожидает ввода-вывода, ЦП вовремя освобождает соответствующие ресурсы кванта времени и выделяет квант времени другим процессам или потокам, чтобы можно было полностью использовать ресурсы ЦП. Поэтому, если большая часть ЦП потребляется на ожидание ввода-вывода (wa), даже если показатель простоя ЦП (id) равен 0%, это не означает, что ресурсы ЦП исчерпаны полностью, если приходит новая задача, ЦП все еще имеет Энергию для выполнения задач. Как показано ниже:

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

Связь между состоянием потока и вводом-выводом в Java

Когда мы просматриваем состояние потока Java с помощью jstack, мы видим различные состояния потока. Когда происходит ожидание ввода-вывода (например, удаленный вызов), в каком состоянии находится поток, заблокированный или ожидающий?

Ответ — состояние Runnable, не правда ли, это немного неожиданно! Фактически, на уровне операционной системы состояние Runnable Java включает в себя не только состояние Running, но также состояние Ready (состояние готовности, ожидание планирования ЦП) и состояние ожидания ввода-вывода.

Как показано на рисунке выше, в аннотации состояния Runnable четко указано, что потоки, выполняемые на уровне JVM, могут ожидать других ресурсов на уровне операционной системы. Если ожидающим ресурсом является ЦП, поток на уровне операционной системы находится в состоянии готовности, ожидая планирования со стороны ЦП; если ожидающим ресурсом является ресурс ввода-вывода, такой как дисковая сетевая карта, поток на уровне операционной системы находится в состоянии ожидания ввода-вывода, ожидая завершения операции ввода-вывода.

Кто-то может спросить, почему у Java-потока нет выделенного состояния выполнения?

В настоящее время большинство основных операционных систем выполняют циклическое планирование задач в форме квантования времени, которое обычно очень короткое, около десятков миллисекунд, то есть поток может выполнять только десятки миллисекунд на ЦП. Затем ЦП планирует перевести его в состояние Готов, ожидая повторного выполнения ЦП, и поток быстро переключится между состояниями Готов и Выполняется. Как правило, состояние потока JVM в основном используется для мониторинга и доступно для просмотра пользователями. Когда вы видите, что состояние потока — Выполняется, это означает, что состояние потока уже было переключено N раз. Поэтому бессмысленно добавлять потоку состояние Running.

Глубокое понимание модели сетевого ввода-вывода

Пять моделей сетевого ввода-вывода Linux включают: синхронный блокирующий ввод-вывод, синхронный неблокирующий ввод-вывод, мультиплексирующий ввод-вывод, управляемый сигналами ввод-вывод и асинхронный ввод-вывод.

написать впереди

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

Разъем: Socket можно понимать как конечные точки связи в двух приложениях, соответственно, когда два приложения осуществляют сетевое взаимодействие. При общении приложение записывает данные в сокет, а затем отправляет данные в сокет другого приложения через сетевую карту. Удаленная связь протоколов HTTP и TCP, которую мы обычно называем, нижний уровень реализован на основе Socket. Пять моделей сетевого ввода-вывода также должны реализовать сетевое взаимодействие на основе сокетов.

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

Пространство ядра и пространство пользователя: В Linux стабильность прикладных программ намного меньше, чем у программ операционной системы.Чтобы обеспечить стабильность операционной системы, Linux различает пространство ядра и пространство пользователя. Таким образом, можно понять, что пространство ядра запускает программы и драйверы операционной системы, а пространство пользователя запускает приложения. Таким образом Linux изолирует программы и приложения операционной системы, предотвращая влияние приложений на стабильность самой операционной системы. Это также основная причина, по которой система Linux очень стабильна. Все операции с системными ресурсами выполняются в пространстве ядра, например, чтение и запись файлов на диск, выделение и утилизация памяти, вызовы сетевого интерфейса и т. д. Следовательно, в процессе чтения сетевого ввода-вывода данные не считываются напрямую с сетевой карты в буфер приложения в пользовательском пространстве, а сначала копируются с сетевой карты в буфер пространства ядра, а затем копируются из ядра в пользовательское пространство. пространство, буфер приложения. Для процесса записи сетевого ввода-вывода процесс обратный: сначала данные копируются из буфера приложения в пространстве пользователя в буфер ядра, а затем данные из буфера ядра отправляются через сетевую карту.

Синхронный блокирующий ввод-вывод

Давайте сначала рассмотрим традиционный блокирующий ввод-вывод. В Linux все сокеты по умолчанию находятся в режиме блокировки. Когда пользовательский поток вызывает системную функцию read(), ядро ​​начинает подготовку данных (получение данных из сети).После подготовки ядром данные копируются из ядра в буфер приложения в пользовательском пространстве. копирование данных завершено, запрос возвращается. Весь процесс, от инициирования запроса на чтение до завершения копирования из ядра в приложение, блокируется. Для повышения производительности каждому соединению может быть выделен поток. Поэтому в сценарии большого количества подключений требуется большое количество потоков, что приведет к огромным потерям производительности, что также является самым большим недостатком традиционного блокирующего ввода-вывода.

Синхронный неблокирующий ввод-вывод

Пользовательский поток возвращается сразу же после инициирования запроса на чтение, не дожидаясь, пока ядро ​​подготовит данные. Если запрос на чтение не считывает данные, пользовательский поток продолжит опрос, чтобы инициировать запрос на чтение, и опрос не будет остановлен до тех пор, пока данные не поступят (ядро готово для данных). Хотя модель неблокирующего ввода-вывода позволяет избежать большого количества потоков, вызванных проблемами блокировки потоков, частые повторные опросы значительно увеличивают количество запросов, а потребление ЦП также очевидно. Эта модель редко используется в практических приложениях.

Мультиплексная модель ввода-вывода

Модель мультиплексного ввода-вывода построена на функциях разделения мультиплексных событий select, poll и epoll. Прежде чем инициировать запрос на чтение, обновите список мониторинга сокетов для выбора, а затем дождитесь возврата функции выбора (этот процесс является блокирующим, поэтому мультиплексный ввод-вывод также является блокирующей моделью ввода-вывода). Когда в сокет поступают данные, функция выбора возвращается. В этот момент пользовательский поток официально инициирует запрос на чтение для чтения и обработки данных. В этом режиме используется выделенный поток мониторинга для проверки нескольких сокетов, и если в сокет поступают данные, они будут переданы рабочему потоку для обработки. Поскольку процесс ожидания поступления данных Socket занимает очень много времени, этот метод решает проблему, заключающуюся в том, что для соединения Socket требуется один поток в модели блокирующего ввода-вывода, и отсутствует проблема потери производительности ЦП, вызванная занятым опросом в модели ввода-вывода с блокировкой. неблокирующая модель ввода-вывода. Существует много практических сценариев применения модели мультиплексирования ввода-вывода, например, хорошо известные Java NIO, Redis и Netty, коммуникационная среда, используемая Dubbo, используют эту модель.

На следующем рисунке показан подробный процесс программирования Socket на основе функции select.

Модель ввода-вывода, управляемая сигналами

Модель ввода-вывода, управляемая сигналом, процесс приложения использует функцию sigaction, и ядро ​​​​возвратится немедленно, то есть процесс приложения не блокируется, когда ядро ​​​​готовит данные. После того, как ядро ​​подготовит данные, оно отправляет сигнал SIGIO процессу приложения, после получения которого данные копируются в процесс приложения.

Таким образом, загрузка процессора высока. Однако в этом режиме в случае большого количества IO-операций может произойти переполнение очереди сигналов и потеря сигнала, что приведет к катастрофическим последствиям.

Модель асинхронного ввода-вывода

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

Понятно, что модель асинхронного ввода-вывода очень производительна. Однако до сих пор приложения с асинхронным вводом-выводом и моделью ввода-вывода, управляемой сигналом, встречаются редко, а традиционные модели блокирующего ввода-вывода и мультиплексного ввода-вывода по-прежнему являются основным направлением текущих приложений. Модель асинхронного ввода-вывода была введена после версии Linux 2.6. В настоящее время многие системы еще не созрели для поддержки модели асинхронного ввода-вывода. Многие сценарии приложений используют мультиплексный ввод-вывод вместо модели асинхронного ввода-вывода.

Как избежать сбоев системы, вызванных проблемами ввода-вывода

Для операции доступа к дисковым файлам можно использовать метод пула потоков, а поток можно настроить на выход в онлайн, чтобы избежать загрязнения всего пула потоков JVM, что приведет к исчерпанию ресурсов потоков и ЦП. .

Для удаленных вызовов между сетями. Чтобы избежать полного сбоя связи между вызовами между службами, следует установить разумное значение TImeout, а в сценариях с высокой степенью параллелизма можно использовать механизм прерывателя цепи. Механизм изоляции потоков принят в рамках одной и той же JVM, а потоки разделены на несколько групп, а разные группы потоков обслуживают разные классы и методы соответственно, чтобы избежать воздействия на все потоки в JVM из-за сбоя небольшого функциональная точка.

Кроме того, очень важны идеальный мониторинг эксплуатации и технического обслуживания (дисковый ввод-вывод, сетевой ввод-вывод) и APM (полный мониторинг производительности канала), которые могут дать раннее предупреждение и предотвратить проблемы до их возникновения, а также могут помочь нам быстро обнаружить проблемы при сбоях. происходить.

Прочитав три вещи ❤️

Если вы считаете, что этот контент весьма полезен для вас, я хотел бы пригласить вас сделать мне три небольших одолжения:

  1. Лайки, репосты и ваши "лайки и комментарии" - движущая сила моего творчества.

  2. Обратите внимание на общедоступный номер 『яванская гнилая свиная кожа’, время от времени делясь оригинальными знаниями.

  3. В то же время вы можете рассчитывать на последующие статьи🚀

Автор статьи: Фэн Тао из публичного аккаунта: Путь к продвинутым архитекторам