Приложение постепенно перестаёт принимать соединения. ss показывает это — в чём дело?
Веб-приложение за часы становится неотзывчивым и в итоге отклоняет новые соединения. Перезапуск лечит на время. Поставьте диагноз по состоянию сокетов ниже; назовите причину и где находится баг.
$ ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
1240 CLOSE-WAIT
14 ESTAB
6 TIME-WAIT
1 LISTEN
$ ss -tanp state close-wait '( sport = :8080 )' | head -2
Recv-Q Send-Q Local Address:Port Peer Address:Port Process
1 0 10.0.3.7:8080 10.0.3.90:41022 users:(("app",pid=811,fd=193))
$ ls /proc/811/fd | wc -l
1207
Что происходит и где баг?
Сокеты копятся в CLOSE_WAIT на собственном :8080 приложения: пир прислал FIN, а приложение так и не вызвало close(), чтобы отправить свой FIN. Это баг приложения — оно течёт сокетами и дескрипторами, пока не упрётся в лимит fd и не перестанет принимать. Много TIME_WAIT, наоборот, нормально на закрывшем первым.
- ✗Путать
CLOSE_WAIT(локальное приложение не закрыло) сTIME_WAIT(нормально) - ✗Считать это тюнингом ядра, а не отсутствующим
close()в приложении - ✗Поднимать
ulimitна fd, маскируя утечку, которая всё равно растёт
- →Почему большое число
TIME_WAITобычно нормально на стороне, закрывшей первой? - →Как найти путь кода, текущий сокетами, по списку fd?
Read the state counts first. A socket enters CLOSE_WAIT when the remote peer has sent its FIN (it is done) and the kernel is now waiting for the local application to call close() so it can send its own FIN. 1240 sockets stuck there means the app is accepting connections, the clients hang up, and the app never closes its end — the fingerprint of a socket / file-descriptor leak in your own code. The /proc/811/fd count of 1207 confirms it: descriptors climb until the process hits its ulimit -n and can no longer accept(), which is why it stops taking connections and a restart temporarily "fixes" it.
Do not confuse this with TIME_WAIT: TIME_WAIT appears on whichever side closes first and is a normal, self-clearing state (a 2·MSL wait). A large TIME_WAIT count is usually benign; a large CLOSE_WAIT count is almost always an application bug.
# Which process and how fast is it leaking?
$ watch -n5 "ls /proc/811/fd | wc -l" # count trends upward → leak
# The bug: a handler that returns without closing the connection/response.
# e.g. a missing defer resp.Body.Close() / try-with-resources / finally close().
The fix is in the application, not the kernel: ensure every accepted connection (and every client it opens downstream) is closed on all paths — defer/finally/with. Raising the fd limit only delays the crash.