Потоки застряли в java.net.SocketInputStream.socketRead0 | Кодементор

Что делает API java.net.SocketInputStream.socketRead0()? Почему он часто появляется в нескольких дампах потоков? Это что-то, о чем мне нужно беспокоиться? Каковы возможные решения этой проблемы? Найдем ответы на эти вопросы.

Что делает API SocketInputStream.socketRead0()?
Всегда легко запомнить новые концепции через аналогии из реальной жизни. Предположим, вы звоните по телефону своей жене или девушке. Как только звонок будет соединен, если она будет в хорошем / хорошем настроении, вы сразу же получите ответ «Привет, дорогая (или дорогая, или милая), как дела?». 😃. Если ваш звонок был подключен, когда она выполняла какую-то работу (скажем, она в своем офисе, забирает детей, занимается в спортзале…), ее ответ может быть с задержкой, чтобы сказать «Привет, дорогая (или дорогая, или милая)…». . Предположим, что ваш звонок был подключен, когда она была в сердитом/плохом настроении, тогда ответ может быть непредсказуемым. Только Бог знает. Вы можете получить ответ через несколько секунд/минут (или даже вызов может быть повешен) 😃. Таким образом, время, которое вы ожидаете с момента подключения вызова до момента, когда вы повесите трубку, в основном API socketRead0(). (Спасибо Дугласу Спату из IBM за этот прекрасный пример, объясняющий API SocketRead0().)

Ваше приложение может взаимодействовать с несколькими удаленными приложениями через различные протоколы, такие как: SOAP, REST, HTTP, HTTPS, JDBC, RMI… все соединения проходят через уровень JDK java.net для выполнения более низких операций TCP-IP/Socket. На этом уровне API SocketInputStream.socketRead0() используется для чтения и получения данных удаленным приложением. Некоторые удаленные приложения могут отвечать немедленно, некоторым может потребоваться время для ответа, а некоторые приложения могут вообще не отвечать. Пока ваше приложение не прочитает данные ответа полностью, поток вашего приложения будет застревать в этом API java.net.SocketInputStream.socketRead0().

Пример трассировки стека дампа потока
Ниже приведен пример трассировки стека, показывающий потоки, застрявшие в API SocketInputStream.socketRead0. Вы можете заметить, что независимо от потоков протокола они застревают в SocketInputStream.socketRead0() API.

Stacktrace1.JPG
Рис. Поток RMI застрял в API SocketInputStream.socketRead0()

Stacktrace2.JPG
Рис. Соединение с базой данных Oracle зависло в API SocketInputStream.socketRead0()

Stacktrace3.JPG
Рис. RabbitMQ застрял в API SocketInputStream.socketRead0()

Stacktrace4.JPG
Рис. Выполнение оператора IBM DB2 зависло в API SocketInputStream.socketRead0()

Решения
Если ваш поток застревает в API SocketInputStream.socketRead0 и не восстанавливается в течение более длительного периода, то клиент, который инициировал транзакцию, не увидит никакого ответа на своем экране. Это может озадачить, запутать пользователя. Если несколько потоков зависают в SocketInputStream.socketRead0 API и не восстанавливаются в течение более длительного периода времени, это может создать серьезные проблемы с доступностью для вашего приложения.

Здесь мы намечаем несколько возможных решений для решения этой проблемы:

  1. Настройки времени ожидания инструмента

    1.1. Настройки сети JVM

    1.2. setSoTimeout

    1.3. JDBC

    1.4. Oracle JDBC

    1.5. Вебсфера

    1.6. Ось2

  2. Проверить подключение к сети

  3. Работа с удаленным приложением

  4. Неблокирующий HTTP-клиент

# 1. Настройки таймаута инструмента
Большинство приложений не устанавливают соответствующие параметры времени ожидания для восстановления из SocketInputStream.socketRead0, поэтому они в конечном итоге застревают в этом API на длительный период. Установка соответствующего тайм-аута — отличный механизм самозащиты, который должно использовать каждое приложение. Вот несколько настроек тайм-аута, которые вы можете применить к своему приложению, если посчитаете нужным:

1.1. Настройки сети JVM
Вы можете передать эти два мощных сетевых свойства тайм-аута, которые могут быть глобально применимы ко всем обработчикам протоколов, которые используют java.net.URLConnection:

а. -Dsun.net.client.defaultConnectTimeout
б. -Dsun.net.client.defaultReadTimeout
sun.net.client.defaultConnectTimeout указывает время ожидания (в миллисекундах) для установления соединения с хостом. Например, для http-соединений это тайм-аут при установлении соединения с http-сервером. Для ftp-соединения это тайм-аут при установлении соединения с ftp-серверами.

sun.net.client.defaultReadTimeout указывает время ожидания (в миллисекундах) при чтении из входного потока при установлении соединения с ресурсом.

Подробнее о сетевых настройках JVM можно найти здесь.

1.2. setSoTimeout
Если вы напрямую программируете с помощью сокетов, вы можете установить тайм-аут для сокета, вызвав setSoTimeout() API.

В этот API вы можете передать значение времени ожидания в миллисекундах. Если удаленное приложение не отвечает в течение указанного времени ожидания, будет выдано исключение java.net.SocketTimeoutException. Это исключение освободит поток, позволяя ему работать с другими вызовами. Примечание. Если значение тайм-аута передается как 0, то оно интерпретируется как бесконечный тайм-аут, это означает, что поток никогда не будет тайм-аутом.

1.3. JDBC
Если вы используете JDBC (Java DataBase Connectivity) для подключения, вы можете рассмотреть возможность установки значения тайм-аута с помощью API setQueryTimeout().

Этот API устанавливает количество секунд, в течение которых драйвер JDBC будет ожидать получения результатов из базы данных. Если лимит превышен, выбрасывается исключение SQLTimeoutException. Драйвер JDBC применяет это ограничение к методам execute, executeQuery() и executeUpdate(). По умолчанию нет ограничений на количество времени, отведенное для выполнения выполняемого оператора.

1.4. Oracle JDBC
Если вы подключаетесь к базе данных Oracle и видите множество потоков, застрявших в SocketInputStream.socketRead0() API, вы можете рассмотреть возможность передачи системного свойства -Doracle.jdbc.ReadTimeout.

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

1.5. Вебсфера
Если ваше приложение работает в IBM Websphere, вы можете установить следующие свойства:

а. Администратор может задать пользовательское свойство источника данных webSphereDefaultQueryTimeout.

б. Второе свойство, syncQueryTimeoutWithTransactionTimeout, также можно задать как настраиваемое свойство источника данных. С этим набором WebSphere рассчитает время, оставшееся до истечения времени ожидания транзакции (если выполняется в рамках глобальной транзакции), и автоматически установит время ожидания запроса на это значение.

1.6. Ось2
Вы также можете установить свойство «readTimeout» в наборе транспортных политик HTTP для клиента веб-службы или установить «тайм-аут» в org.apache.axis2.context.MessageContext в коде приложения.

# 2. Проверка подключения к сети
Потоки, которые не восстанавливаются из SocketInputStream.socketRead0 API, также могут возникать из-за проблем с сетевым подключением или балансировщиками нагрузки. В прошлом мы видели, что иногда удаленное приложение может не выдавать соответствующие пакеты ACK или FIN. Возможно, вам придется привлечь сетевых инженеров или группу поддержки провайдеров облачного хостинга для устранения проблемы.

Со своей стороны вы можете использовать инструменты отслеживания TCP/IP, такие как Wireshark, для просмотра пакетов, отправляемых по сети между вами и удаленным приложением. Это может помочь вам определить, находится ли проблема на вашей стороне сети или на другой стороне сети.

# 3. Работа с удаленным приложением
Иногда вполне возможно, что транзакции могут замедляться из-за проблем с производительностью в удаленном приложении. В этих обстоятельствах вам необходимо сообщить удаленному приложению о замедлении и работать с ними, чтобы решить проблему.

# 4. Неблокирующий HTTP-клиент
Вы также можете рассмотреть возможность использования неблокирующих клиентских библиотек HTTP, таких как Гризли или же Нетти у которых нет блокирующих операций для зависания потока. Но это решение скорее стратегическое, которое включает в себя изменения кода и тщательное тестирование.

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

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *