Однажды днем несколько лет назад программисты в компании тихонько набирали коды, как вдруг одновременно у многих людей зазвонили мобильные телефоны. Я думал, что мне платят, но я очень счастлив! Открыл, оказалось предупреждение
Исправление проблем
Аварийные сообщения: «Слишком много потоков, превышение порога», «Слишком низкая частота простоя ЦП». Когда я открыл систему мониторинга, все 20 сервисных узлов службы заказов вышли из строя, и служба не ответила.
Количество потоков каждого узла Springboot достигло максимального значения. Но память кучи JVM и GC не являются явно ненормальными. Уровень простоя ЦП в основном составляет 0%, но загрузка ЦП невелика, но время ожидания ввода-вывода очень велико. Ниже приведен снимок экрана с выполнением верхней команды для просмотра состояния ЦП:
На изображении выше мы видим:
Уровень простоя ЦП составляет 0% (идентификатор красного прямоугольника на рисунке выше)
Использование ЦП составляет 22% (красное поле на рисунке выше - это us 13% плюс sy 9%, us можно понимать как ЦП, занятый пользовательским процессом, а sy можно понимать как ЦП, занимаемый системным процессом)
Доля времени, которое ЦП тратит на ожидание операций дискового ввода-вывода, составляет 76,6 % (красная рамка wa на приведенном выше рисунке).
На данный момент можно определить, что проблема должна возникать в ожидании ввода-вывода. С помощью системы мониторинга и команды jstack наконец удалось установить, что проблема возникла при записи файла. Большое количество операций чтения и записи на диск приводит к исчерпанию ресурсов потока JVM (обратите внимание, что это не означает, что ЦП системы исчерпан). В конце концов, служба заказа не может ответить на запрос вышестоящей службы.
IO, вещи, которые вы не знаете
Поскольку ввод-вывод оказывает такое большое влияние на производительность и стабильность системы, давайте рассмотрим его более подробно.
Так называемая операция ввода/вывода (ввода/вывода) на самом деле представляет собой поведение передачи данных ввода и вывода. Программисты больше всего обеспокоены дисковым вводом-выводом и сетевым вводом-выводом, потому что эти две операции ввода-вывода наиболее непосредственно и тесно связаны с приложениями.
Дисковый ввод-вывод: ввод и вывод диска, например, передача данных между диском и памятью.
Сетевой ввод-вывод: передача данных по сетям между различными системами, например вызовы удаленного интерфейса между двумя системами.
На следующем рисунке показан конкретный сценарий, в котором в приложении происходит ввод-вывод:
На приведенном выше рисунке мы можем понять конкретную сцену операции ввода-вывода. В процессе запроса может выполняться множество операций ввода-вывода:
-
Сетевой ввод-вывод происходит, когда страница запрашивается на сервере
-
Сетевой ввод-вывод будет происходить при удаленных вызовах между службами
-
Сетевой ввод-вывод будет происходить, когда приложение обращается к базе данных.
-
Дисковый ввод-вывод происходит при запросе или записи данных в базу данных
Отношения между вводом-выводом и процессором
Многие осадные львы понимают, что если показатель простоя ЦП равен 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 (полный мониторинг производительности канала), которые могут дать раннее предупреждение и предотвратить проблемы до их возникновения, а также могут помочь нам быстро обнаружить проблемы при сбоях. происходить.
Прочитав три вещи ❤️
Если вы считаете, что этот контент весьма полезен для вас, я хотел бы пригласить вас сделать мне три небольших одолжения:
-
Лайки, репосты и ваши "лайки и комментарии" - движущая сила моего творчества.
-
Обратите внимание на общедоступный номер 『яванская гнилая свиная кожа’, время от времени делясь оригинальными знаниями.
-
В то же время вы можете рассчитывать на последующие статьи🚀
Автор статьи: Фэн Тао из публичного аккаунта: Путь к продвинутым архитекторам