Тема ЗАБЛОКИРОВАНА на InetAddress.getByName 0.0.0.0

Я столкнулся с ситуацией, когда мое приложение зависает при запуске из-за ситуации «тупика», связанной с InetAddress.getByName, но мне не ясно, как это исправить.

Чтобы дать некоторый контекст, два задействованных потока не находятся под моим прямым контролем:

  • 1 поток (ЗАБЛОКИРОВАН) запускает HTTP-сервер Prometheus
  • 1 поток (RUNNABLE) связан с клиентской библиотекой ZIO Http, вызывая некоторые вещи Netty в качестве клиента (не сервера)

Соответствующий код 1-го потока:

new InetSocketAddress("0.0.0.0", somePort)

И второй:

static final InetAddress INET6_ANY = InetAddress.getByName("::")
static final InetAddress INET_ANY = InetAddress.getByName("0.0.0.0")

Я читал, что использование InetAddress может повлечь за собой некоторую блокировку и т. д. но почему оно зависает навсегда? Тем более, что мы имеем в виду локальный адрес 0.0.0.0, а не какой-то удаленный адрес.

Это приложение работает в контейнере в Kubernetes, если это может что-то объяснить.

$ cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.42.126.184   my-app-756f44d67-tgr5b

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

Может ли это быть «ошибкой», например, неправильным использованием библиотек? Или я, возможно, упускаю что-то, что необходимо определить, чтобы такой код работал в контексте Kubernetes?


Для полноты вот дамп этих двух потоков.

Тот, который ЗАБЛОКИРОВАН:

"ZScheduler-Worker-6" #30 daemon prio=5 os_prio=0 cpu=276.23ms elapsed=7541.20s tid=0x00007f7bd54d4880 nid=0x55 waiting for monitor entry  [0x00007f7b663f4000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at jdk.internal.loader.NativeLibraries.loadLibrary([email protected]/Unknown Source)
    - waiting to lock <0x00000000a02e9a90> (a java.util.HashSet)
    at jdk.internal.loader.NativeLibraries.loadLibrary([email protected]/Unknown Source)
    at jdk.internal.loader.NativeLibraries.findFromPaths([email protected]/Unknown Source)
    at jdk.internal.loader.NativeLibraries.loadLibrary([email protected]/Unknown Source)
    at jdk.internal.loader.NativeLibraries.loadLibrary([email protected]/Unknown Source)
    at jdk.internal.loader.BootLoader.loadLibrary([email protected]/Unknown Source)
    at java.net.InetAddress.<clinit>([email protected]/Unknown Source)
    at java.net.InetSocketAddress.<init>([email protected]/Unknown Source)
    at io.prometheus.metrics.exporter.httpserver.HTTPServer$Builder.makeInetSocketAddress(HTTPServer.java:209)
    at io.prometheus.metrics.exporter.httpserver.HTTPServer$Builder.buildAndStart(HTTPServer.java:197)
    at io.opentelemetry.exporter.prometheus.PrometheusHttpServer.<init>(PrometheusHttpServer.java:71)
    at io.opentelemetry.exporter.prometheus.PrometheusHttpServerBuilder.build(PrometheusHttpServerBuilder.java:68)
    at com.myapp.metrics.sdk.PrometheusMetricReader$.$anonfun$startReader$2(PrometheusMetricReader.scala:21)
    at com.myapp.metrics.sdk.PrometheusMetricReader$$$Lambda$1109/0x00007f7b7840e078.apply(Unknown Source)
    at zio.ZIOCompanionVersionSpecific.$anonfun$attempt$1(ZIOCompanionVersionSpecific.scala:100)
    at zio.ZIOCompanionVersionSpecific$$Lambda$430/0x00007f7b782ba000.apply(Unknown Source)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:904)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:967)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:967)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.evaluateEffect(FiberRuntime.scala:381)
    at zio.internal.FiberRuntime.evaluateMessageWhileSuspended(FiberRuntime.scala:504)
    at zio.internal.FiberRuntime.drainQueueOnCurrentThread(FiberRuntime.scala:220)
    at zio.internal.FiberRuntime.run(FiberRuntime.scala:139)
    at zio.internal.ZScheduler$$anon$4.run(ZScheduler.scala:478)

Тот самый "запирающий":

"ZScheduler-Worker-20" #44 daemon prio=5 os_prio=0 cpu=191.27ms elapsed=7541.20s tid=0x00007f7bd54e2e70 nid=0x63 in Object.wait()  [0x00007f7b655e3000]
   java.lang.Thread.State: RUNNABLE
    at io.netty.channel.epoll.LinuxSocket.unsafeInetAddrByName(LinuxSocket.java:364)
    - waiting on the Class initialization monitor for java.net.InetAddress
    at io.netty.channel.epoll.LinuxSocket.<clinit>(LinuxSocket.java:42)
    at jdk.internal.loader.NativeLibraries.load([email protected]/Native Method)
    at jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open([email protected]/Unknown Source)
    at jdk.internal.loader.NativeLibraries.loadLibrary([email protected]/Unknown Source)
    - locked <0x00000000a02e9a90> (a java.util.HashSet)
    at jdk.internal.loader.NativeLibraries.loadLibrary([email protected]/Unknown Source)
    at java.lang.ClassLoader.loadLibrary([email protected]/Unknown Source)
    at java.lang.Runtime.load0([email protected]/Unknown Source)
    at java.lang.System.load([email protected]/Unknown Source)
    at io.netty.util.internal.NativeLibraryUtil.loadLibrary(NativeLibraryUtil.java:36)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0([email protected]/Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke([email protected]/Unknown Source)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke([email protected]/Unknown Source)
    at java.lang.reflect.Method.invoke([email protected]/Unknown Source)
    at io.netty.util.internal.NativeLibraryLoader$1.run(NativeLibraryLoader.java:430)
    at java.security.AccessController.executePrivileged([email protected]/Unknown Source)
    at java.security.AccessController.doPrivileged([email protected]/Unknown Source)
    at io.netty.util.internal.NativeLibraryLoader.loadLibraryByHelper(NativeLibraryLoader.java:422)
    at io.netty.util.internal.NativeLibraryLoader.loadLibrary(NativeLibraryLoader.java:388)
    at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:218)
    at io.netty.channel.epoll.Native.loadNativeLibrary(Native.java:323)
    at io.netty.channel.epoll.Native.<clinit>(Native.java:85)
    at io.netty.channel.epoll.Epoll.<clinit>(Epoll.java:40)
    at zio.http.netty.ChannelFactories$Client$.$anonfun$fromConfig$4(ChannelFactories.scala:83)
    at zio.http.netty.ChannelFactories$Client$$$Lambda$966/0x00007f7b783d3e60.apply(Unknown Source)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:967)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:967)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:890)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:1024)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:967)
    at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:967)
    at zio.internal.FiberRuntime.evaluateEffect(FiberRuntime.scala:381)
    at zio.internal.FiberRuntime.evaluateMessageWhileSuspended(FiberRuntime.scala:504)
    at zio.internal.FiberRuntime.drainQueueOnCurrentThread(FiberRuntime.scala:220)
    at zio.internal.FiberRuntime.run(FiberRuntime.scala:139)
    at zio.internal.ZScheduler$$anon$4.run(ZScheduler.scala:478)

Они связывают разные порты? Возможно, один сервер заблокирован в ожидании, пока другой сервер освободит привязку адреса.

Rogue 23.02.2024 17:12

Поток @Rogue 1 (Prometheus) ожидает привязки к (фиксированному) порту, но другой не должен AFAIK, это просто клиентский код для вызова других HTTP-сервисов.

Gaël J 23.02.2024 17:15

Таким образом, вы почти всегда будете привязываться к порту (если только вы не выполняете какие-либо действия уровня <3), а для HTTP это обычно порт 80. Если вы не настроили эти две службы для использования разных портов, то это это было бы первое, что я бы отрегулировал.

Rogue 23.02.2024 17:17

Извините, если я не ясно выразился, но только один поток является сервером (порт, к которому он привязан, я контролирую). Другой - это просто клиентский код AFAIK, но, возможно, я что-то упускаю.

Gaël J 23.02.2024 17:31

Ага, нет, это моя собственная оплошность в отношении «клиента». Первый поток BLOCKED находится внутри статического инициализатора InetAddress, поэтому любой поток, который попытается использовать InetAddress, также станет «заблокированным». Вам действительно нужно будет решить первую цепочку BLOCKED этого #loadLibrary звонка.

Rogue 23.02.2024 17:46

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

dan1st 23.02.2024 19:25
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
6
201
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Обновлять

Это исправлено в репозитории Netty и будет включено в Netty 4.1.108.Final.

Что происходит

Из ваших дампов тем я вижу следующее:

  • В заблокированном потоке инициализатор класса InetAddress ожидает загрузки собственной библиотеки, но не может этого сделать, поскольку другой поток загружает собственную библиотеку.
  • Другой поток пытается использовать InetAddress как часть загрузки собственной библиотеки. В частности, инициализатор класса Native класса Нетти загружает собственную библиотеку ( netty_transport_native_epoll), которая, в свою очередь, вызывает LinuxSocket (или, по крайней мере, инициализирует ее), что требует загрузки InetAddress.

Итак, проблема в том, что Netty использует InetAddress при загрузке собственной библиотеки, что может произойти во время инициализации.

Обходной путь

Вы можете убедиться, что InetAddress полностью инициализирован, прежде чем давать Нетти возможность что-либо сделать. Вы можете сделать это, запустив InetAddress.getLocalHost(); в начале файла main. Это должно быть до того, как Netty где-либо будет использоваться, и она должна инициализироваться InetAddress

Собственно решение проблемы

Вы можете отправить отчет об ошибке команде Netty (или даже написать запрос на включение самостоятельно).

Одно из решений, которое они могли бы реализовать, — это инициализировать InetAddress перед загрузкой собственных библиотек (которые полагаются на их загрузку/загрузку).

Например, они могли бы добавить вызов InetAddress.getLocalHost(); в класс Native перед фактической загрузкой данных (например, в начале Native.loadNativeLibrary).

В качестве альтернативы можно даже изменить что-то, что вообще не требует загрузки встроенной библиотеки InetAddress. Однако у меня недостаточно знаний о Netty (внутренностях), чтобы судить об этом.

Спасибо! Вероятно, сейчас пойдет обходной путь. Для справки, я открыл проблему на Netty: github.com/netty/netty/issues/13871.

Gaël J 24.02.2024 16:07

Ошибка исправлена ​​Нетти: github.com/netty/netty/pull/13879 🎉

Gaël J 01.03.2024 21:55

Приятно слышать.

dan1st 02.03.2024 11:21

Другие вопросы по теме