Как Nginx обрабатывает тысячи одновременных запросов
Подписывайтесь на телеграм-канал usr_bin, где я публикую много полезного по Linux, в том числе ссылки на статьи в этом блоге.
Понимание событийно-управляемой неблокирующей архитектуры Nginx
Традиционный подход — один запрос на процесс
В традиционных веб-серверах каждому запросу назначается отдельный поток (или процесс) для обработки параллельных запросов. Эти потоки тратят вычислительные ресурсы, такие как память и процессор, блокируя (ожидая) завершения запросов во время сетевых операций или операций ввода-вывода. После завершения запросов соответствующие потоки уничтожаются и создаются заново при поступлении новых запросов. Такое уничтожение и создание новых потоков требует дополнительных вычислительных ресурсов, что приводит к снижению производительности.
Один запрос — один поток
- Сервер прослушивает новые запросы на подключение.
- При поступлении нового запроса сервер принимает его, создает новый выделенный процесс и назначает запрос на обработку.
- Процесс продолжает ожидать (блокироваться) завершения внешних операций, таких как дисковый или сетевой ввод-вывод. Это может происходить несколько раз во время обработки запроса.
- После завершения запроса процесс ждет, чтобы увидеть, планирует ли клиент начать новый запрос (поддерживать соединение). Если клиент закрывает соединение или происходит тайм-аут, сервер уничтожает процесс и прослушивает новые запросы на соединение.
Архитектура Nginx
Nginx (Engine-X) разработан с использованием четырех основных компонентов:
- Главный процесс — отвечает за загрузку конфигурации сервера и создание дочерних процессов (следующие три типа).
- Загрузчик кэша — загружает кэш диска в память и затем завершает работу.
- Диспетчер кэша — запускается через определенные промежутки времени и удаляет ненужные (или устаревшие) записи из кэша диска, чтобы поддерживать его размер в пределах настроенного лимита.
- Рабочий процесс — обрабатывает сетевые подключения, сетевой и дисковый ввод-вывод. Он выполняет всю работу! При запуске сервера главный процесс инициализирует несколько рабочих процессов в зависимости от конфигурации сервера.
Подход Nginx — событийно-ориентированный
В отличие от традиционных серверов, Nginx не создаёт отдельный процесс или поток для каждого входящего запроса. Вместо этого каждый рабочий процесс прослушивает события, генерируемые новыми входящими запросами. Рабочие процессы принимают запросы на подключение и начинают обработку.
Однако вместо того, чтобы блокировать (или ждать) ответ во время сетевого и дискового ввода-вывода, рабочий процесс немедленно принимает другой запрос на соединение из входящего пула или приступает к продолжению обработки запросов, которые завершили свой дисковый и сетевой ввод-вывод и ожидают дальнейшего выполнения.
Почему это быстрее?
На традиционных серверах, где процесс создаётся для каждого запроса на подключение, каждому процессу требуются циклы CPU. Переключение контекста выделяет эти циклы CPU каждому процессу. Переключение контекста требует сохранения текущего состояния запущенного процесса для последующего восстановления и последующей загрузки состояния другого процесса для его запуска. Переключение контекста, а также дополнительные накладные расходы и ресурсы, необходимые для создания процесса для каждого запроса на подключение, влияют на общую производительность сервера.
В Nginx это не так, поскольку фиксированное количество рабочих процессов (равное количеству ядер процессора) обрабатывает все входящие запросы. Это сокращает количество переключений контекста, поскольку каждому рабочему процессу выделяется своё ядро процессора. Как уже упоминалось, это также позволяет избежать дополнительного потребления ресурсов и накладных расходов на создание процессов, поскольку рабочие процессы создаются при запуске сервера. Это позволяет Nginx обрабатывать сотни тысяч одновременных подключений.
На этом все! Спасибо за внимание! Если статья была интересна, подпишитесь на телеграм-канал usr_bin, где будет еще больше полезной информации.