Я настраиваю Рабочий процесс NGINX таким образом, чтобы значения параметров `worker_processes`, `worker_connections` и `worker_rlimit_nofile` точно совпадали, и Epoll срабатывал в цикле событий. Благодаря этому я использую Ядра процессора обеспечивать высокую эффективность, масштабировать количество одновременных подключений в соответствии с планом и поддерживать низкие задержки в пиковые моменты нагрузки.
Центральные пункты
Следующие ключевые моменты помогут тебе сразу сориентироваться при настройке надежной конфигурации рабочих серверов NGINX.
- рабочие_процессы связать с количеством логических ядер, в идеале с параметром „auto“.
- рабочие_соединения установить так, чтобы с запасом покрывать реальные пиковые нагрузки.
- rlimit_nofile и увеличить лимиты ОС в соответствии с объемом трафика.
- epoll и включить multi_accept, чтобы эффективно использовать цикл событий.
- Нагрузочные испытания продолжать и постепенно вносить точные корректировки.
Архитектура NGINX: что такое «мастер» и «рабочий»
Я разделяю задачи на Мастер И в отношении рабочих процессов всё ясно: главный процесс загружает конфигурации, открывает сокеты и запускает процессы, в то время как рабочие процессы обрабатывают запросы в цикле событий. Каждый рабочий процесс работает автономно, реагирует на события и может управлять тысячами соединений, не вызывая блокировок. Эта модель показывает себя с лучшей стороны, если я правильно распределяю нагрузку по ядрам ЦП и оптимально использую цикл событий с помощью epoll. При этом я учитываю, что каждый дополнительный прокси-хоп потребляет ресурсы соединения, что отражается на ограничениях. Тот, кто понимает эти роли, осознанно принимает решения о Ресурсы и позволяет своевременно предотвращать возникновение узких мест.
Правильное сочетание трёх ключевых директив
Я считаю рабочие_процессы, worker_connections и worker_rlimit_nofile никогда не настраиваются изолированно, а всегда в комплексе. Общее количество возможных соединений определяется как количество рабочих процессов, умноженное на количество соединений на каждый рабочий процесс; на этой основе я вывожу ограничения для файловых дескрипторов. Если эти настройки не согласованы, я сталкиваюсь с ошибкой „too many open files“ или наблюдаю жесткие таймауты. Для высокой нагрузки мне нужна слаженная цепочка: достаточное количество процессов, щедрые настройки connections, грамотно увеличенный rlimit_nofile и подходящие параметры ОС. Так я предотвращаю ситуацию, когда слишком маленький Ограничение полностью лишил способности к размножению.
worker_processes: выбрать конкретное количество
Я установил рабочие_процессы Как правило, этот параметр устанавливается в значение „auto“, чтобы NGINX мог определить количество логических ядер процессора и задействовать каждое из них. Использование одного рабочего процесса на каждое ядро позволяет избежать ненужных смен контекста и равномерно распределить нагрузку, что обеспечивает предсказуемое время отклика. На машинах с очень большим количеством ядер я специально тестирую и меньшее количество рабочих процессов, чтобы сравнить количество попаданий в кэш и загрузку ядер. Если метрики показывают, что ядра перегружены или растёт количество промахов TLB, я постепенно корректирую количество рабочих процессов. Сначала измеряю, потом меняю — так я обеспечиваю надёжные Результаты.
worker_connections: плановое увеличение количества соединений
Я выбираю рабочие_соединения в зависимости от целевого трафика и набора протоколов, часто начиная с 2048 или 4096. Для API с высокой нагрузкой я рассматриваю значение 8192, при условии, что это соответствует ограничениям ОС и объему оперативной памяти. Каждое увеличение я проверяю с помощью нагрузочных тестов, поскольку открытые соединения занимают память и влияют на поведение верхних уровней. Если преобладают SSL-рукопожатия или большие загрузки, я ориентируюсь скорее на профили загрузки ЦП и ввода-вывода, а не только на «голые» цифры количества соединений. Таким образом я гарантирую, что значение, заданное для каждого рабочего процесса, Вместимость останется пригодным для реального использования.
Синхронизация параметра `worker_rlimit_nofile` и ограничений ОС
Я позабочусь о том, чтобы rlimit_nofile по крайней мере, покрывает расчетную общую пропускную способность и часто настраивается с запасом. Для сценариев с обратным прокси я закладываю второй дескриптор на каждый клиентский соединительный канал для связи с верхним уровнем. Соответственно, я обычно устанавливаю значение rlimit_nofile в два раза выше ожидаемого числа одновременных соединений. Я повышаю ограничения ядра и пользователя (ulimit -n, fs.file-max) таким образом, чтобы NGINX действительно мог использовать эти значения. Если в журнале ошибок появляются сообщения об открытых файлах, я оперативно увеличиваю эти значения и наблюдаю за Латентность под нагрузкой — ещё раз.
Блок «События»: эффективное использование epoll и multi_accept
Я активирую в блоке «События» epoll и устанавливаю для параметра multi_accept значение „on“, чтобы рабочие процессы принимали ожидающие соединения за один проход. Epoll снижает накладные расходы при большом количестве одновременных сокетов и хорошо сочетается с неблокирующей архитектурой NGINX. Эти параметры окупаются во время пиковых нагрузок, поскольку позволяют ускорить фазу принятия соединений и быстрее перейти к их фактической обработке. Для Linux это мой стандартный набор настроек, который я изменяю лишь в редких особых случаях. Те, кто хочет углубиться в тему, могут сравнить модель цикла событий с Пул потоков против цикла событий и делает из этого вывод выводы для своего окружения.
Аффинность к процессору: привязка рабочих процессов к ядрам
Я установил affinity_процессора_рабочего_процесса целенаправленно, если рабочие нагрузки являются постоянными и ограниченными ресурсами ЦП. Схему привязки я распределяю с помощью битовых масок, чтобы избежать смены контекста и повысить локальность кэша. При наличии четырёх ядер я распределяю маски таким образом, чтобы каждый рабочий процесс получал собственное ядро. Затем я проверяю показатели промахов кэша, медианные задержки и 99-й процентиль, чтобы чётко увидеть эффект. Более подробные объяснения по аффинности и NUMA вы найдёте в кратком изложении на Практика использования аффинности процессора, что при доработке Рабочий-макеты.
Планирование пропускной способности: резерв пропускной способности и нагрузочные тесты
При соединениях я планирую Буфер , который значительно превышает наблюдаемые пиковые значения, чтобы кратковременные всплески не приводили к непосредственному превышению лимитов. Если в качестве отправной точки удвоить пиковую нагрузку, то во многих сценариях у меня будет солидный запас прочности. При сильных колебаниях трафика я увеличиваю буфер до тех пор, пока 99-й процентиль не станет стабильным. Затем я проверяю узкие места с помощью таких инструментов, как wrk или k6, отслеживаю показатели ошибок и просматриваю открытые соединения в статусе. Только когда показатели становятся стабильными, я целенаправленно увеличиваю или уменьшаю отдельные Значения.
Настройка и примеры расчетов
Я рассчитываю пропускную способность соединений как произведение количества рабочих процессов на количество соединений на каждый рабочий процесс и на основе этого устанавливаю более высокие ограничения. При четырёх ядрах ЦП с параметром «auto» и 4096 соединениями на каждый рабочий процесс по моим расчётам получается 16 384 одновременных соединений. В сценариях использования прокси я обычно устанавливаю rlimit_nofile на уровне 32 768 или выше, чтобы учесть сокеты верхнего уровня. Для небольших машин с двумя ядрами часто достаточно 2048 соединений на каждый рабочий процесс, при условии, что доля загрузки и TLS остается умеренной. Следующая таблица поможет сориентироваться в начальные значения:
| Ядра процессора | рабочие_процессы | worker_connections (Начало) | Мин. rlimit_nofile (ориентировочное значение) | Подсказка |
|---|---|---|---|---|
| 2 | авто (≈2) | 2048 | ≥ 4096 | Резерв запланировать для TLS/прокси |
| 4 | авто (≈4) | 4096 | ≥ 16 384 | При использовании прокси часто применяется коэффициент 2 для FD |
| 8 | авто (≈8) | 4096-8192 | ≥ 32768 | Испытание нагрузкой принимает решение об увеличении |
| 16+ | автомобиль, возможно, меньше | 8192+ | ≥ 65535 | Проводить тестирование с учетом склонности и сдержанности |
Рабочие процессы NGINX и источники: правильная оценка сценариев
Я различаю статическую доставку, работу в режиме обратного прокси и нагрузку на API-шлюз, поскольку они Рабочий-конфигурации. Статический контент задействует меньше ресурсов, тогда как TLS, сжатие и соединения с вышестоящими серверами более интенсивно загружают ЦП и файловые дескрипторы. Чем больше размеры SSL-ключей и чем больше количество рукопожатий, тем больше преимуществ дает настройка „один рабочий процесс на ядро“. Крупные загрузки смещают акцент на ввод-вывод, поэтому я уделяю больше внимания параметрам rlimit_nofile и сетевым буферам. При заметных задержках при приеме данных или получении ответов от бэкенда мне помогает обзор Очереди и задержки, чтобы избежать узких мест целевой решить.
Рабочий процесс: шаг за шагом к более быстрому серверу
Я начну с анализа текущего состояния всех значимых Значения В файле nginx.conf проверяю количество ядер процессора, настройки ulimit и параметры ядра. Затем устанавливаю для параметра worker_processes значение «auto», задаю, например, значение worker_connections равным 4096 и значительно увеличиваю значение rlimit_nofile. В блоке Events я включаю epoll и multi_accept, а затем перезагружаю сервер и проверяю логи. Далее следуют нагрузочные тесты в воспроизводимых условиях, в ходе которых я отслеживаю время отклика, частоту ошибок и количество открытых соединений. При доработке я всегда изменяю только одну переменную, документирую каждый шаг и проверяю последствия в Метрики.
Хостинг-среда: ресурсы, ядро, сеть
Я слежу за тем, чтобы было достаточно CPU-ядра, достаточное количество оперативной памяти, быстрые SSD-накопители или NVMe и актуальное ядро Linux. Только в этом случае epoll, современные стеки TCP и эффективные функции разгрузки будут работать надежно. Сетевые параметры, такие как somaxconn и tcp_max_syn_backlog, я настраиваю в соответствии с целевым количеством соединений, чтобы сократить длину очередей приема. Поставщик с высокой производительностью ввода-вывода и свободным доступом к настройкам системы явно окупает себя. Сравнения показывают, что сервисы с постоянной Ресурсы Значительно расширить возможности NGINX.
Стратегия Keepalive: соединения с клиентом и с вышестоящим сервером
Я сознательно использую Keepalive в качестве средства регулирования пропускной способности и задержки. На стороне клиента я настраиваю keepalive_timeout не слишком высоким, чтобы неактивные сокеты не занимали место без необходимости рабочие_соединения блокировать. Значения в диапазоне 10–30 с часто позволяют мне найти оптимальный баланс между повторным использованием и затратами ресурсов. С помощью keepalive_requests Я ограничиваю количество запросов на одно соединение, чтобы сократить количество длительных сеансов и избежать перегрузки памяти. На стороне исходного сервера (обратный прокси) я поддерживаю постоянные соединения с keepalive в блоке upstream, благодаря чему отпадает необходимость в обменных процедурах и настройке TCP. При этом я консервативно масштабирую количество на каждый бэкенд в соответствии с его пропускной способностью (max_conns), иначе я сам создаю очереди при передаче данных вверх по потоку. Важно: каждый сокет Keepalive считается открытым соединением и требует FD; я учитываю это в rlimit_nofile и мои расчеты запаса по высоте.
Оптимизация списков: reuseport, backlog и стратегия Accept
Я равномерно распределяю нагрузку на опоры, для чего SO_REUSEPORT активировать (listen … reuseport). Таким образом, каждый рабочий процесс получает собственную очередь принятия запросов, что снижает вероятность возникновения „громовых стад“ и позволяет избежать появления «горячих точек». В сочетании с multi_accept я заметно ускоряю этап принятия. В списке —отставание (listen … backlog=) и соответствующие параметры ядра (somaxconn, tcp_max_syn_backlog) я устанавливаю на достаточно высокие значения, чтобы пиковые нагрузки не терялись на входе сокета. Параметр отсрочка откладывает подтверждение до поступления данных — при большом количестве кратковременных запросов это может помочь, в остальных случаях я сравниваю результаты в тестах. Независимо от того, accept_mutex Нужно ли это, я выясняю с помощью теста производительности: при использовании reuseport он, как правило, не нужен; без reuseport он может повысить справедливость, но требует дополнительных усилий по координации. В этом вопросе я принимаю решения на основе данных, а не по интуиции.
Настройка стабильных таймаутов и очередей
Я установил Тайм-ауты таким образом, чтобы медленные клиенты не перегружали рабочие процессы: client_header_timeout и таймаут_тела_клиента Я делаю их достаточно короткими, чтобы избежать зависаний, но при этом достаточно длинными для реальных пользователей. send_timeout защищает от заблокированных ответов, направляемых клиенту. В контексте прокси я определяю proxy_connect_timeout, proxy_read_timeout и proxy_send_timeout строго, чтобы зависшие бэкенды не парализовали работу фронтенда. В случае бэкендов с ограниченной степенью параллелизма я использую очередь в блоке upstream с таймаутом, чтобы сглаживать пиковые нагрузки и контролируемо выдавать код ошибки 503 вместо того, чтобы привязывать всех рабочих процессов к ожидающим сокетам upstream. Кроме того, я стабилизирую работу с помощью limit_req (Burst/Delay) и limit_conn оптимизировать маршруты, чтобы отдельные клиенты или боты не потребляли непропорционально много ресурсов.
Буферизация, sendfile и AIO: осознанный выбор способов ввода-вывода
Я установил sendfile для статических файлов и объединяю его с tcp_nopush/tcp_nodelay в зависимости от нагрузки, чтобы эффективно объединять пакеты или сократить задержки при интерактивных операциях. Для больших файлов я использую directio начиная с определенного порога, чтобы избежать загрязнения кэша и не допустить вытеснения страниц из кэша. В режиме прокси я решаю, следует ли proxy_buffering помогает (быстрая передача клиенту, развязанное чтение из источника) или же при потоковых нагрузках мне лучше proxy_request_buffering уменьшите, чтобы запустить загрузку заранее. Размеры proxy_buffers, proxy_buffer_size и большие буферы заголовков клиентов Я сознательно регулирую это, чтобы объем используемой памяти на одно соединение не вырос до заоблачных размеров. Для оптимизации нагрузки на процессор при доступе к файлам я рассматриваю возможность aio (нативный или потоковый), но тщательно протестируйте, поскольку характеристики цикла событий и ввода-вывода взаимно влияют друг на друга.
HTTP/2/HTTP/3 и TLS: влияние на производительность рабочих процессов
Я принимаю во внимание, что HTTP/2 и HTTP/3 Изменение динамики соединений: многие запросы выполняются в режиме потоки через небольшое количество соединений TCP или QUIC. Это снижает количество соединений, но увеличивает нагрузку на ЦП и объем памяти, требуемый на каждое соединение (мультиплексирование, сжатие заголовков, TLS/QUIC). Моя рабочие_соединения Поэтому я не считаю это автоматически означающим „одинаковое количество запросов“. Я замечаю, что параллельные потоки за каждое соединение и пас keepalive_timeout и, при необходимости,. http2_max_concurrent_streams . На стороне TLS я получаю преимущества благодаря возобновлению сеанса (билеты/кеш) и OCSP-Stapling; таким образом я избегаю дорогостоящих рукопожатий и снижаю задержки. Обратная сторона: более длительные keepalive занимают FD и ОЗУ — поэтому я планирую rlimit_nofile а также квоты на память с реалистичными резервами. Для алгоритмов шифрования, интенсивно использующих ресурсы ЦП, стоит провести тестирование с использованием аффинности и современных средств ускорения криптографических операций.
Мониторинг: состояние, журналы и метрики
Я обеспечиваю прозрачность с помощью оптимизированного Статус-Endpoint (например, stub_status), чтобы просматривать активные соединения, состояния «Чтение», «Запись» и «Ожидание», а также принятые запросы. В логах я стараюсь минимизировать лишнюю информацию: компактный log_format с указанием времени, статуса, времени прохождения по восходящему потоку и количества байтов — этого достаточно для большинства анализов. При очень высоком QPS я выборочно отключаю журнал доступа (в зависимости от местоположения) или буферизую журналы асинхронно, чтобы операции ввода-вывода не тормозили работу. Журнал ошибок я настраиваю на предупредить или ошибка и переключайтесь на него ненадолго только для проведения целенаправленных анализов отладка. Я постоянно сопоставляю задержки (медиана, 95-й и 99-й процентили), открытые соединения, коэффициенты ошибок бэкенда и нагрузку на ЦП для каждого рабочего процесса — на основе этого я определяю необходимые корректировки трёх ключевых директив и своевременно выявляю признаки перегрузки.
Контейнеры и виртуальные среды: четкое соблюдение ограничений
Я проверяю в контейнерах cgroup-Установите ограничения для ЦП, оперативной памяти и PID и сопоставьте их с настройками NGINX. ulimit -n должно быть достаточно высоким внутри контейнера, иначе мои настройки rlimit_nofile не будут работать. При использовании квот на ЦП (например, 2 vCPU) я устанавливаю рабочие_процессы соответственно, чтобы планировщик не создавал искусственных заторов. При работе вблизи сети я получаю выгоду от более низкой задержки, связанной с накладными расходами, в сетевых режимах „host“, тогда как оверлеи означают дополнительные прыжки. На хостах с поддержкой Multi-NUMA я обращаю внимание на аффинность и разъёмы памяти, чтобы рабочие процессы не работали на разных узлах. То же самое касается аффинности IRQ и RPS/XPS: если пути от сетевой карты через IRQ до ядра рабочего процесса совпадают, пиковые значения задержки заметно снижаются.
Жизненный цикл соединения: эфемерные порты, состояние TIME_WAIT и резервные порты
Я планирую достаточно временные порты (ip_local_port_range), когда NGINX выступает в роли активного клиента для Upstreams. При очень высокой пропускной способности соединений я избегаю чрезмерной смены портов с помощью Upstream-Keepalive, что позволяет сократить стеки TIME_WAIT. К параметрам ядра, связанным с „повторным использованием“, я подхожу с осторожностью; современные стеки уже оптимизируют многое внутренне. Более стабильным является управление продолжительностью соединений с помощью разумных значений keepalive и таймаута, а также reuseport использовать для справедливого распределения. При расчете пропускной способности я всегда учитываю не только клиентов, но и сторону «upstream» — зачастую именно там FD являются фактическим ограничивающим фактором, а не «frontdoor».
Плавная перезагрузка и развертывание без простоев
Я использую модель «мастер-рабочий» для плавная перезагрузка: Мастер загружает новые конфигурации, старые рабочие узлы завершают работу, а новые плавно принимают на себя их функции. С помощью таймаут_выключения_работника я даю запросам время завершиться корректно, не блокируя ресурсы. Развертывания без простоев на Upstream я сочетаю с проверками работоспособности и proxy_next_upstream-правила, чтобы отдельные неисправные бэкенды не повышали общую задержку. При внесении изменений в конфигурацию я всегда изменяю только один параметр и проверяю результаты в логах и метриках — так я избегаю ошибок, связанных с путаницей, и обеспечиваю воспроизводимость производительности.
Краткое резюме
Я подключаю рабочие_процессы Укажите количество ядер (в идеале — «auto»), настройте параметр `worker_connections` в соответствии с пиковой нагрузкой и значительно увеличьте значение `rlimit_nofile` вместе с ограничениями ОС. В блоке событий я использую `epoll` и `multi_accept`, проверяю все с помощью воспроизводимых нагрузочных тестов, а затем постепенно корректирую настройки. Для прокси-нагрузок я учитываю дополнительные дескрипторы и тестирую аффинность процессора, если нагрузки остаются постоянными. Правильно настроенный стек с подходящим ядром, быстрым вводом-выводом и оптимальными сетевыми параметрами играет решающую роль. Вот как я добиваюсь NGINX надежно в диапазон производительности, который требуется для сложных веб-страниц и API.


