Проблема
При нагрузке свыше 8 000 RPS на входной балансировщик Nginx бэкенды на Go начали отдавать 502 Bad Gateway. Логи Nginx заполнились сообщениями:
connect() failed (99: Cannot assign requested address) while connecting to upstream
Одновременно средняя задержка ответа сервиса (p99) выросла с 8 мс до 140 мс.
Анализ сокетов
Смотрим статус сокетов через ss -s:
Total: 34100
TCP: 32800 (estab 4200, closed 27500, orphaned 12, timewait 27480)
Более 27 000 сокетов висят в состоянии TIME_WAIT!
Смотрим доступный диапазон портов:
sysctl net.ipv4.ip_local_port_range
# net.ipv4.ip_local_port_range = 32768 60999
Доступно всего 28 231 локальный порт. Nginx на каждый входящий HTTP-запрос открывал новое TCP-соединение к бэкенду (127.0.0.1:8080), отправлял один запрос, получал ответ и закрывал соединение. Сокет переходил в TIME_WAIT на 60 секунд (стандартный TCP_TIMEWAIT_LEN).
Правильная конфигурация Nginx Upstream
Чтобы Nginx держал пул постоянных соединений к бэкендам, необходимо обязательно настроить keepalive в блоке upstream и передать правильные заголовки в блоке location:
upstream backend_cluster {
server 127.0.0.1:8080 max_fails=3 fail_timeout=10s;
# Размер пула постоянных keepalive соединений на каждый воркер
keepalive 128;
keepalive_requests 10000;
keepalive_timeout 60s;
}
server {
listen 80;
server_name api.internal;
location / {
proxy_pass http://backend_cluster;
# КРИТИЧЕСКИ ВАЖНО: переопределить HTTP/1.0 на HTTP/1.1
proxy_http_version 1.1;
# Очистить заголовок Connection (по умолчанию Nginx шлет "close")
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Тюнинг ядра для плотного сетевого трафика
На уровне sysctl добавляем возможность переиспользования TIME_WAIT сокетов:
# /etc/sysctl.d/99-network-highload.conf
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10240 65535
net.ipv4.tcp_fin_timeout = 15
Применяем: sysctl -p /etc/sysctl.d/99-network-highload.conf.
Результат
- Количество сокетов в
TIME_WAITупало с 27 500 до менее 350. - Задержка p99 нормализовалась до 4.5 мс при стабильных 12 000 RPS.
- Загрузка CPU на воркерах Nginx упала на 35% благодаря отсутствию постоянного TCP 3-way handshake.