← Back to blog

NGINX: Connection Lifecycle

⚙️ Теория

🔄 Connection Lifecycle в NGINX

Базовая цепочка обработки:

acceptreadcreate requestrun phasescontentwritekeepalive/close

По шагам:

  1. accept
    worker принимает TCP-соединение на listening socket.
  2. read
    читает старт запроса и заголовки из сокета клиента.
  3. create request
    создаёт внутренний объект request и связывает его с connection.
  4. run phases
    выполняет цепочку фаз HTTP (rewrite, access, try_files, proxy и т.д.).
  5. content
    формирует ответ локально или идёт в upstream.
  6. write
    отправляет ответ клиенту (частями, неблокирующе).
  7. keepalive/close
    либо оставляет соединение открытым для следующего запроса, либо закрывает.

🧩 Разница: connection, request, subrequest

connection:

  • TCP-соединение (сокет, состояние keepalive, таймауты);
  • может жить дольше одного HTTP-запроса.

request:

  • один HTTP-запрос внутри соединения;
  • у HTTP/1.1 обычно по одному за раз на connection (практически без pipelining);
  • завершается после отправки ответа.

subrequest:

  • внутренний дочерний запрос NGINX (например auth_request, SSI, mirror);
  • не отдельное клиентское соединение;
  • нужен для внутренней логики до финального ответа основного request.

Что нужно уметь объяснить

Почему keepalive уменьшает CPU?

  • меньше новых accept() и TCP/TLS handshakes;
  • меньше системных вызовов на открытие/закрытие соединений;
  • меньше затрат ядра на выделение/очистку socket state;
  • больше полезной работы на запрос, меньше служебных операций.

Итог: при том же трафике CPU обычно ниже, а latency стабильнее.

Что происходит при медленном клиенте?

  • NGINX готовит ответ быстро, но отправляет его медленно (slow receiver);
  • сокет часто «не готов к записи», данные дольше висят в буферах;
  • connection дольше занят, растут active connections и память на буферы;
  • при большом числе таких клиентов можно упереться в worker_connections (лимит учитывает все типы соединений на worker).

Что происходит при медленном upstream?

  • клиент пришёл быстро, но backend отвечает медленно;
  • request дольше остаётся "в полёте", растёт очередь активных запросов;
  • увеличиваются latency, возможны upstream timed out и 502/504;
  • воркеры не блокируются как поток, но держат больше незавершённых состояний.

🧪 Практика

1. Отключить keepalive

В server или http:

keepalive_timeout 0;

keepalive_timeout 0 отключает keep-alive для клиентских соединений.

Перезагрузить:

nginx -t && sudo nginx -s reload

2. Сравнить RPS и CPU

До/после изменения keepalive:

wrk -t4 -c200 -d30s http://127.0.0.1/

Параллельно смотреть CPU:

top -H -p $(pgrep -d',' -f 'nginx: worker process')

Сравнивать:

  • requests/sec;
  • latency p95/p99;
  • загрузку CPU по worker.

3. Сделать upstream с искусственной задержкой

Поднять простой sleep backend на 127.0.0.1:18080:

python3 -c '
from http.server import BaseHTTPRequestHandler, HTTPServer
import time

class H(BaseHTTPRequestHandler):
    def do_GET(self):
        time.sleep(1)  # искусственная задержка backend
        body = b"slow backend\n"
        self.send_response(200)
        self.send_header("Content-Type", "text/plain")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

HTTPServer(("127.0.0.1", 18080), H).serve_forever()
'

Для NGINX:

upstream slow_backend {
    server 127.0.0.1:18080;
}

location /slow {
    proxy_pass http://slow_backend;
    proxy_read_timeout 5s;
}

Затем нагрузка:

wrk -t4 -c200 -d30s http://127.0.0.1/slow

4. Посмотреть сводку сокетов

ss -s

Полезно сравнить до/после:

  • TCP: inuse;
  • estab;
  • timewait;
  • orphaned.

🧾 Вывод

connection lifecycle помогает понять, где именно теряется производительность: на установке соединений, на клиентской стороне или в upstream.
keepalive экономит CPU за счёт повторного использования соединений, но медленные клиенты и медленные backend всё равно увеличивают время жизни запросов и нагрузку на воркеры.
Практически важно не только измерять RPS, но и одновременно смотреть latency, CPU и состояние TCP (ss -s) — только так видно реальную причину деградации.


📚 Ссылки


Проверка источников

NGINX: Connection Lifecycle | Aleksandr Suprun