1. 1
  2. 2
  3. 3
  4. 4
  5. 5

# конспект · шаг 5 из 5

Конспект: WebSocket, таймауты, буферизация

выжимка - её можно унести в заметки

WebSocket, таймауты и буферизация - три темы, где «работает на тесте, падает на проде».

WebSocket

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

location /ws/ {
    proxy_pass http://app:8000;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host       $host;
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}

map нужен, чтобы обычные запросы не получали Connection: upgrade.

Свежее: proxy_http_version 1.1; из старых руководств больше не нужен - с nginx 1.29.7 значение по умолчанию и так 1.1, Connection: close бэкенду не шлётся, а keepalive в upstream включён сам.

Таймауты

Директива Что ограничивает Умолчание
proxy_connect_timeout установку соединения с бэкендом 60s (больше 75s нельзя)
proxy_send_timeout передачу запроса бэкенду 60s
proxy_read_timeout ожидание между частями ответа 60s

proxy_read_timeout - это пауза между чтениями, а не общее время ответа. Отсюда 504 у долгих отчётов: бэкенд молчит 90 секунд, потом отдаёт всё сразу - но nginx уже ушёл.

Буферизация

По умолчанию nginx буферизует ответ: забирает у бэкенда быстро, отдаёт клиенту медленно и тем освобождает бэкенд. Замер на 50 МБ и клиенте 50 КБ/с:

настройка бэкенд
proxy_buffering on отдал за 0,26 с и свободен
proxy_buffering off держал соединение, пока клиент не ушёл
on + proxy_max_temp_file_size 0 держал, как при off

Выигрыш даёт временный файл: буферов всего восемь по 16 КБ.

Поток она задерживает не всегда. Время до первого байта на ответе из четырёх кусков по секунде:

как устроен ответ on off
Transfer-Encoding: chunked 0,0015 с 0,0014 с
объявлен Content-Length 4,005 с 0,0015 с
chunked плюс gzip 4,003 с 0,0015 с

Чанкованный поток проходит и с буферизацией. Копится ответ с объявленной длиной и ответ под сжатием. Потоковый маршрут лечат точечным proxy_buffering off с gzip off; либо заголовком X-Accel-Buffering: no от самого приложения.

Ловушки джокера

  • 502 upstream sent too big header - раздутые cookie сессии не влезли в proxy_buffer_size. Лечится увеличением, а не перезапуском бэкенда.
  • WebSocket «рвётся раз в минуту» - proxy_read_timeout по умолчанию 60 секунд, замер даёт 60,1. Любой кадр в канале сбрасывает отсчёт, поэтому таймаут поднимают и добавляют ping.
  • Одного Connection мало - без Upgrade бэкенд отвечает 200 вместо 101.
  • Любой свой proxy_set_header в блоке уносит заголовки апгрейда, включая безобидную строку с Host.
  • proxy_buffering off глобально «чтобы шло сразу» - ошибка: чанкованный ответ идёт сразу и без этого, а воркер бэкенда окажется занят на всё время скачивания у всех маршрутов.
  • Keepalive к бэкенду работает только через upstream. Замер: шесть запросов к литеральному адресу - шесть соединений, к той же машине внутри группы - одно.
  • С 1.31.0 nginx отклоняет h2/h3-запросы с хоп-хоп-заголовками, поэтому WebSocket живёт отдельным HTTP/1.1-соединением - это норма, а не поломка.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.