Короткий ответ
TIME_WAIT — состояние TCP-соединения после его закрытия инициирующей стороной, в котором сокет удерживается некоторое время (обычно 60 секунд в Linux по умолчанию), чтобы корректно обработать запоздавшие пакеты; при очень высокой частоте открытия новых исходящих соединений с одного и того же IP к одному и тому же адресу/порту назначения можно исчерпать весь диапазон доступных эфемерных портов, из-за чего новые соединения не могут быть установлены.
Как это работает подробнее
Каждое исходящее TCP-соединение занимает уникальную четвёрку (локальный IP:порт, удалённый IP:порт); пока соединение находится в TIME_WAIT, этот локальный порт в сочетании с конкретным удалённым адресом считается занятым и не может быть переиспользован для нового соединения с тем же удалённым адресом, пока не истечёт тайм-аут — при тысячах коротких соединений в секунду к одному и тому же backend это быстро исчерпывает диапазон портов из net.ipv4.ip_local_port_range.
Пример кода
ss -tan state time-wait | wc -l
cat /proc/sys/net/ipv4/ip_local_port_rangeЧто использовать на практике
- Практика. решение — не плодить короткие соединения: использовать постоянные (keep-alive) соединения и пулы соединений вместо открытия нового TCP-соединения на каждый запрос, расширить диапазон эфемерных портов, включить net.ipv4.tcp_tw_reuse (аккуратно, с пониманием последствий для NAT-сценариев) и балансировать нагрузку на несколько исходящих IP при необходимости.
Подводные камни
- Риск. Частая ошибка — включать net.ipv4.tcp_tw_recycle (убран из современных ядер Linux из-за проблем за NAT) или бездумно сокращать тайм-аут TIME_WAIT глобально, вместо того чтобы устранить первопричину — отсутствие переиспользования соединений на уровне приложения.