Назад | Перейти на главную страницу

Затяжное соединение TCP в состоянии LISTEN

Мое Java-приложение иногда может быть убито внешним скриптом. Это можно сделать либо с помощью SIGTERM, либо с помощью SIGKILL.

Приложение представляет собой сервер, который получает много подключений в секунду, и его можно убить, пытаясь их обслужить.

Я хотел бы перезапускать приложение всякий раз, когда оно завершается, поэтому я подготовил для этой цели сценарий.

Проблема в том, что после того, как приложение было убито, новый экземпляр приложения не может привязаться к порту, используемому предыдущим экземпляром, потому что «Адрес уже используется». Процесс предыдущего экземпляра был определенно завершен, в любом случае порт прослушивания, вызывающий нарушение, все еще существует, но он назначен на bash (или sh на других машинах).

Очевидно, моя цель - перезапустить приложение и позволить ему успешно привязаться к предыдущему адресу.

Я пробовал подождать более 200 секунд перед перезапуском, но безрезультатно, в любом случае я не могу позволить себе так долго ждать.

Я столкнулся с этой проблемой на всех машинах, на которых я запускал приложение (это причальный сервер с java 1.6).

Любое предложение приветствуется, спасибо,

Сильвио

РЕДАКТИРОВАТЬ Завершение процесса jvm - это не обычный способ выхода из приложения, это используется только в случае проблем (OutOfMemoryErrors). И мне никогда не нужно убивать его с помощью SIGKILL, потому что SIGTERM всегда достаточно, я прибегаю к SIGKILL только в случае сбоя SIGTERM, чего никогда не было. Я работаю над долгосрочным решением, а тем временем мне нужно поддерживать работу приложения, накладывая стежки здесь и там.

РЕДАКТИРОВАТЬ Для большей ясности: это netstat -tunap | Перед тем, как убить процесс, я вижу строку grep:

tcp6       0      0 :::8898       :::*        LISTEN      22709/java

и это после убийства процесса

tcp6       0      0 :::8898       :::*        LISTEN      23665/sh

обратите внимание, что процесс с PID 22709 убит и ушел, но порт все еще существует (но заблокирован sh)

ОБНОВИТЬ после того, как я убью свое приложение, с помощью netstat я могу увидеть длинный список ожидающих соединений в состоянии CLOSE_WAIT с моим IP в качестве пункта назначения. Кроме того, я вижу, что процесс sh в состоянии LISTEN прослушивает мой порт: когда я его убиваю, процесс сна заменяет его и прослушивает тот же порт: когда я наконец завершаю этот процесс сна, порт освобождается, и я могу успешно перезапустить мой сервер. Это могло бы быть решением для освобождения моего порта, но я боюсь, что автоматическое завершение процессов для освобождения порта немного рискованно.

Сервер по-прежнему ожидает некоторых пакетов от клиентов после закрытия прослушивающих сокетов и сохраняет назначенный порт. Приложение может использовать опцию сокета SO_REUSEADDR, чтобы разрешить немедленное повторное использование адреса сокета.

Вот выдержка из моей справочной страницы по Linux ip (7):

Связанный адрес локального сокета TCP недоступен в течение некоторого времени после закрытия, если не установлен флаг SO_REUSEADDR. Следует соблюдать осторожность при использовании этого флага, поскольку он снижает надежность TCP.

Приложение или сервер приложений могут иметь параметр конфигурации для использования этого параметра сокета.

На самом деле вы не убиваете свое Java-приложение, вы фактически убиваете свой экземпляр виртуальной машины Java (jvm), который, в свою очередь, запускает ваше Java-приложение.

Это не идеальный способ завершить ваш Java-процесс.

если вам нужно убить свой jvm с помощью kill -9, jvm не сможет очистить себя, оставив рабочие ресурсы в подвешенном состоянии. :-(

Добавьте в свое приложение некоторые функции, чтобы завершить его изящно. Если у вас нет выбора, попробуйте убить вас jvm с помощью -15, это может помочь прояснить само себя.

Если ваша java-программа действительно зависает от jvm, вам нужно получить отладчик и уничтожить этих вредителей.

Убить процесс и перезапустить его - это взлом, но это не исправление. Вы должны использовать SIGKILL, только если процесс не отвечает ни на один другой метод.

Я обычно стараюсь

убить -15

тогда убивайте -9 только в крайнем случае.

и по приколу ...

http://www.youtube.com/watch?v=Fow7iUaKrq4

Поскольку вы делаете это только вручную, вам, возможно, придется добавить еще одну проверку.

netstat -p

и убейте pid, связанный с вашим открытым сокетом, даже если это bash или sh.

Также вы упомянули, что в большинстве случаев SIGTERM работает. В этом случае ваше приложение должно поймать SIGTERM и перейти к некоторому изящному коду выхода, который RST закрывает все открытые соединения, а затем закрывает сокет.

HTH

Если у вас есть доступ к исходному коду, вам нужно создать сокет с SO_REUSEADDR вариант, упомянутый Яцеком. Также интересны tcp_tw_recycle и tcp_tw_reuse флаги ядра (в Linux).

Настоящая проблема заключается в дизайне протокола, который вы можете изменить, а можете и не изменить. Интересные темы по теме:

С вашим обновлением у меня есть другое объяснение. Процесс sh, сохраняющий сокет открытым, должен быть дочерним по отношению к вашему приложению, разветвленным после открытия прослушивающего сокета. Он не умер вместе со своим родителем и был принят процессом init.

Вам следует попытаться выяснить, для чего нужен этот процесс оболочки (возможно, какой-то сценарий, запущенный вашим приложением) и почему он не завершается. Может быть, достаточно исправить скрипт, чтобы он завершился после завершения работы, будет достаточно? Или есть способ заставить его не отсоединяться от родителя (он должен умереть вместе с родителем, если он является частью той же группы процессов) или заставить его закрыть все ненужные файловые дескрипторы, унаследованные от родителя.

Вы можете попробовать:

fuser -p $pid_of_the_sh_process

чтобы увидеть, какие еще файлы он оставляет открытыми. Одним из них, скорее всего, будет сценарий оболочки. Зная, что это такое, мы можем найти способ решить проблему.