← Back to notes

NGINX: Connection
Lifecycle


Теория

Connection Lifecycle

acceptreadcreate requestrun phasescontentwritekeepalive/close

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

connection vs request vs subrequest

connection:

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

request:

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

subrequest:

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

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

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

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

При том же трафике CPU ниже, latency стабильнее.

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

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

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

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

Практика

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

keepalive_timeout 0;
nginx -t && sudo nginx -s reload

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

wrk -t4 -c200 -d30s http://127.0.0.1/
top -H -p $(pgrep -d',' -f 'nginx: worker process')

Сравнивать: requests/sec, latency p95/p99, CPU по worker.


3. Upstream с искусственной задержкой

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

class H(BaseHTTPRequestHandler):
    def do_GET(self):
        time.sleep(1)
        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()
'
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.


Ссылки

NGINX: Connection Lifecycle | Aleksandr Suprun