Вопрос или проблема
У нас есть выделенный сервер, который работает нормально и принимает входящие подключения. Он также корректно отправляет и получает данные.
Он отлично работает в течение нескольких часов, но после этого все принятые подключения автоматически истекают, как только сокет принят.
Чем больше он принимает сокетов, тем скорее он переходит в состояние, при котором все новые подключения истекают.
Проблема не в клиенте и не в установленном нами времени таймаута, потому что это происходит сразу после принятия в функции read().
Когда мы перезагружаем сервер, все начинает снова работать прекрасно.
Я думаю, что проблема связана с Linux. Я не очень хорошо знаю Linux, я знаю, что в Linux есть конфигурации, которые ограничивают мой процесс.
Может кто-нибудь порекомендовать материалы для чтения о конфигурациях Linux для TCP-сокетов?
Спасибо.
Не могли бы вы предоставить больше деталей? Какой сервис принимает подключения? Какой дистрибутив используется (пожалуйста, предоставьте вывод команды uname -a)? У вас есть какие-либо данные в журналах сервиса?
Вы можете проверить системные журналы с помощью следующих команд: systemctl status <service>, чтобы увидеть статус сервиса и несколько строк журналов.
И journalctl -x –lines 100 -u <service>, чтобы увидеть последние 100 строк журналов сервиса.
Также команда dmesg или файлы, такие как /var/log/syslog и /var/log/kern.log в случае систем, подобных Debian, могут содержать дополнительную информацию. Или /var/log/messages в случае систем, подобных RHEL.
Проблема также может быть связана с количеством открытых файлов или конфигурацией сервиса (например, пределом соединений в конфигурации базы данных или максимальным количеством потоков в Tomcat). Обычно современные системы не нуждаются в изменении настроек по умолчанию. Но некоторые старые системы могут не открыть новые подключения, если лимит превышен. https://linux.die.net/man/5/limits.conf
Настройки ядра, связанные с сетевыми соединениями: https://docs.kernel.org/networking/ip-sysctl.html
Параметры могут быть настроены для всех интерфейсов или для каждого интерфейса с использованием соответствующего пути. Настройки могут быть изменены на лету двумя способами (например, отключение IPv6):
- echo 1 > /proc/sys/net/ipv6/conf/all/disable_ipv6 – отключает IPv6 для всех интерфейсов до перезагрузки
- sysctl -w net.ipv6.conf.ens160.disable_ipv6=0 >> /etc/sysctl.d/sysctl.conf – отключает IPv6 для интерфейса ens160 и добавляет соответствующую строку в конец /etc/sysctl.d/sysctl.conf. В этом случае настройки будут применены при следующей перезагрузке.
Полезные команды, которые могут помочь: lsof для отображения открытых файлов (соединение также считается файлом) и ss или netstat.
Например, lsof -i:8090 отобразит все соединения с портом 8090. А lsof -p 2345 отобразит все файлы и соединения, открытые процессом с PID 2345.
Обратите внимание, что перед внесением любых изменений в параметры ядра лучше узнать истинную причину.
После месяцев борьбы с этой проблемой я нашел корень проблемы.
Я закрыл fd 0, который является стандартным вводом, и новые входящие сокеты захватывали fd 0, в то время как в моем коде я ожидал, что любой действительный fd больше 0, в противном случае это означает, что он уже закрыт или должен быть исключен из действительных соединений (и я действительно закрываю этот fd, чтобы убедиться 😀 перед исключением).
И это приводило к тому, что fd 0 всегда был закрыт после его захвата.
Это была моя ошибка.
Ответ или решение
Проблема, с которой вы столкнулись, связана с неправильной обработкой дескрипторов файлов в вашем коде. Как вы сами выяснили, закрытие дескриптора 0 (stdin) приводит к тому, что новые входящие сокеты могут перетаскивать этот дескриптор и вы не получаете ожидаемого поведения.
Вот некоторые рекомендации и шаги, которые можно предпринять, чтобы избежать подобных проблем в будущем и лучше понять, как работает управление дескрипторами файлов в Linux:
1. Правильное управление дескрипторами файлов
Дескрипторы файлов (FD) – это абстракции, используемые для доступа к ресурсам системы, таким как сокеты, файлы и устройства. Ваша программа должна учитывать, что FD 0, 1 и 2 зарезервированы для stdin, stdout и stderr соответственно. Закрытие FD 0 означает, что новые входящие сокеты могут занимать этот дескриптор, что вызывает непредсказуемое поведение.
Рекомендации:
- Не закрывайте FD 0, если это не предусмотрено вашей логикой.
- Используйте более высокие дескрипторы для соединений, чтобы избежать подобных конфликтов.
2. Обработка ошибок
Обязательно проверяйте возвращаемые значения из системных вызовов, таких как accept()
, read()
, и других. Это поможет вам быстрее идентифицировать проблемы в случае их возникновения.
3. Настройки лимитов
Проверьте лимиты системы, такие как максимальное количество открытых файлов. Используйте команду ulimit -n
, чтобы просмотреть текущий лимит открытых файлов и, при необходимости, установите более высокий лимит в файле конфигурации /etc/security/limits.conf
.
# Пример для увеличения лимита открытых файлов
* soft nofile 4096
* hard nofile 8192
После внесения изменений перезагрузите систему или заново войдите в сессию, чтобы изменения вступили в силу.
4. Мониторинг и диагностика
- Используйте
lsof
,ss
илиnetstat
для мониторинга открытых соединений и их состояния. Например, командаlsof -i :<port>
поможет вам увидеть все подключения к определенному порту. - Регулярно проверяйте системные логи с помощью команд
journalctl -xe
илиdmesg
, чтобы выявить возможные проблемы, связанные с сетевыми соединениями.
5. Чтение и изучение
Для более глубокого понимания настройки TCP-сокетов в Linux, рассмотрите следующие материалы:
- Документация по сокетам в Linux
- Конфигурационные параметры сетевого ядра в документации kernel.org
- Книга "UNIX Network Programming" от W. Richard Stevens.
Заключение
Ошибки с дескрипторами файлов могут быть неочевидны и трудны для диагностики, особенно если система работает стабильно в течение некоторого времени. Ваша проблема была решена благодаря выявлению неправильно закрываемого дескриптора. В будущем следите за тем, чтобы ваши логики работы с файловыми дескрипторами были надежны, и не забывайте использовать мониторинг, чтобы заранее выявлять проблемы.