Коротко — CLOSE‑WAIT означает, что удалённая сторона (клиент) закрыла соединение (отправила FIN), а приложение на сервере (Xray) не закрыло свой конец (не вызвало close()). Рост CLOSE‑WAIT = утечка сокетов в приложении (или зависшие горутины/блокировки), и это надо диагностировать и лечить на уровне Xray/конфигурации или обновления. Ниже — практический план действий: как диагностировать, временные обходные пути и долгосрочные решения.
1) Что означает CLOSE‑WAIT и почему это важно
- CLOSE‑WAIT = локальное приложение ещё держит сокет после того, как удалённый закрыл соединение. Это не работает на уровне ядра, это приложение должно закрыть. Накопление таких сокетов ведёт к исчерпанию FD/портов и падению производительности.
- TIME‑WAIT — уже нормальное состояние при закрытии активной стороны TCP; его можно уменьшить настройками ядра, но CLOSE‑WAIT лечится в приложении.
2) Быстрая проверка и сбор текущих метрик
Выполните на сервере:
- Посчитать и показать соединения:
ss -tanp | grep CLOSE-WAIT
ss -s
- Посмотреть, какие процессы держат их:
ss -tanp state close-wait
- Список открытых сокетов/файлов процесса Xray:
PID=$(pidof xray)
lsof -nP -p $PID | grep TCP
ls -l /proc/$PID/fd | grep socket
- Посмотреть /proc/net/tcp (inode у сокетов) и соотнести с /proc/$PID/fd:
cat /proc/net/tcp
# сопоставьте inode из /proc/net/tcp со ссылкой socket:[inode] в /proc/$PID/fd
3) Получение стеков goroutine (стек дампов Go) — важно для разработчиков
- Отправьте SIGQUIT процессу, чтобы Go напечатал goroutine dump в stderr / журнал:
kill -QUIT $PID
# затем проверьте вывод в journalctl -u xray -e или в файле лога Xray
- Соберите логи (journalctl / var/log) и файлы, полученные на шагах выше — это пригодится для отчёта о баге.
4) Временные смягчающие меры
- Перезапуск Xray как вынужденная мера (останавливает утечку, но не решает причину):
systemctl restart xray
# Можно настроить systemd timer для перезапуска в критической ситуации, но это временно.
- Увеличьте лимит открытых дескрипторов (но это лишь отсрочит проблему):
ulimit -n 200000
# лучше прописать в systemd Unit: LimitNOFILE=
- Блокируйте/ограничивайте источники, генерирующие много соединений (iptables/nftables, fail2ban), если причина — злостные клиенты.
- Ограничьте количество одновременно открытых соединений для пользователей через policy Xray (чтобы избежать перегрузки от клиентов):
Пример (в конфиге):
"policy": {
"levels": {
"0": {
"handshake": 4,
"connIdle": 300,
"downlinkOnly": 0,
"uplinkOnly": 0
}
},
"system": {
"statsInboundDownlink": false
}
}
(Адаптируйте под вашу версию и структуру конфига.)
5) Kernel tuning (для TIME‑WAIT и общих сетевых проблем — не исправит CLOSE‑WAIT):
- Для уменьшения эффекта TIME_WAIT (только при необходимости):
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_fin_timeout=30
sysctl -w net.ipv4.ip_local_port_range="1024 65000"
sysctl -w net.core.somaxconn=1024
# Не используйте tcp_tw_recycle на серверах за NAT — это ломает работу клиентов за NAT.
6) Обновление и проверка известных багов
- Проверьте релиз‑ноуты xray-core и issue tracker — возможно, это известная утечка в вашей версии (v26.6.1). Часто проблемы с конкретными transport/реализациями (Reality/QUIC/аттачменты) исправляются в новых релизах.
- Обновите Xray (или протестируйте свежую версию в тестовом окружении). Многие утечка‑проблемы исправляются в новых релизах.
- Если после обновления проблема остаётся — откройте issue в репозитории xray с собранными данными: ss/lsof, goroutine dump, конфиг (без секретов), версия. Это ускорит расследование.
7) Что собрать перед обращением за помощью (чтобы сократить время диагностики)
- Версия Xray (xray -version)
- Конфиг (обрезать ключи/сертификаты)
- Результаты:
ss -tanp | grep CLOSE-WAIT > closewait.txt
lsof -nP -p $PID > lsof.txt
ls -l /proc/$PID/fd > fds.txt
kill -QUIT $PID и лог с goroutine dump (journalctl -u xray)
/proc/$PID/status (FDSize) и ulimit -n
journalctl -u xray -b > journal.txt
- Количество открытых файлов: ls /proc/$PID/fd | wc -l
8) Дополнительные проверки
- Если Xray работает в контейнере — проверьте, не исчерпывается ли namespace FD/ресурсы контейнера.
- Проверьте наличие сторонних прокси/iptables конфликтов (например, TPROXY/REDIRECT), которые могут оставлять сокеты в состоянии полузакрытых.
- Посмотрите, не виснут ли горутины в чтении/записи (goroutine dump покажет блокировки).
Резюме / Практические шаги сейчас
1. Соберите данные (ss, lsof, goroutine dump).
2. Временно перезапустите Xray и/или ограничьте коннекты политиками и firewall.
3. Попробуйте обновить Xray до последней стабильной версии.
4. Если после обновления проблема остаётся — откройте issue с приложенными логами/дампами.
Если хотите, я могу помочь пошагово: подскажите вывод ss -tanp | grep CLOSE-WAIT и версию xray -version и (скрытый) конфиг inbound для VLESS+Reality — и я подскажу, какие именно сокеты забирать, как собрать goroutine dump и какие настройки policy/системные параметры рекомендовать.