Теория
Master / Worker
master-процесс:
- читает конфигурацию;
- открывает listening sockets;
- запускает и контролирует
worker-процессы; - выполняет
reload,graceful shutdown,log reopen.
worker-процессы:
- принимают подключения;
- обрабатывают HTTP-запросы;
- работают независимо.
Дефолт worker_processes 1, рекомендуемое значение — auto.
Single-threaded worker
Каждый worker в классической модели NGINX однопоточный: один процесс = один event loop. Нет потока на соединение и постоянных переключений контекста между тысячами потоков.
epoll / kqueue
NGINX использует событийные механизмы ядра:
epollна Linux;kqueueна BSD/macOS.
worker получает от ядра только готовые события: «можно читать» / «можно писать».
accept mutex
accept_mutex снижает thundering herd — сериализует accept() между воркерами и уменьшает конкуренцию за listening socket.
- по умолчанию
accept_mutex off; - на Linux с
EPOLLEXCLUSIVE(ядро ≥ 4.5) включать обычно не нужно; - при
reuseportтоже не требуется.
reuseport
reuseport даёт каждому worker собственный listening socket на одном порту. Распределение делает ядро, что уменьшает lock-contention на этапе accept.
Что нужно уметь объяснить
Почему один worker может держать тысячи соединений?
У worker нет модели «один поток = одно соединение». Один процесс ведёт много сокетов и работает только с теми, которые готовы к чтению/записи.
По шагам:
workerвызываетepoll_wait()и «засыпает».- Ядро будит его при готовых событиях.
workerобрабатывает событие (прочитал/отправил часть данных).- Переходит к следующему готовому сокету.
- Возвращается в
epoll_wait().
Почему worker не блокируется?
- сокеты в неблокирующем режиме;
- если данные не готовы (
EAGAIN/EWOULDBLOCK), worker идёт дальше; - ожидание готовности делает ядро через
epoll_wait()/kevent().
Где возможен contention?
accept()на одном listening socket (смягчаютaccept_mutexиreuseport);- общие структуры в shared memory (
limit_req_zone,keys_zone,upstream zone); - CPU (мало
worker_processes, long requests, перегруженный upstream); - диск и сеть (очереди I/O, packet drops, медленный backend).
Признаки: рост latency p95/p99, падение RPS, рост таймаутов и 5xx.
Практика
1. Количество воркеров
ps aux | grep nginx
2. Event loop через strace
strace -p <pid_worker>
strace -f -e trace=epoll_wait,accept4,recvfrom,sendto -p <pid_worker>
3. Параметры NGINX
worker_processes auto;
events {
worker_connections 4096;
use epoll;
}
worker_connections — максимум всех соединений на worker (клиентские, upstream и т.д.), не только входящих. Дефолт 512.
nginx -t && sudo nginx -s reload
4. Нагрузить и сравнить
ab -n 50000 -c 500 http://127.0.0.1/
wrk -t4 -c400 -d30s http://127.0.0.1/
Сравнивать: latency (p50/p95/p99), requests/sec, ошибки (5xx, timeouts), CPU по воркерам.