Я столкнулся с ситуацией, когда мое приложение зависает при запуске из-за ситуации «тупика», связанной с InetAddress.getByName
, но мне не ясно, как это исправить.
Чтобы дать некоторый контекст, два задействованных потока не находятся под моим прямым контролем:
Соответствующий код 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 1 (Prometheus) ожидает привязки к (фиксированному) порту, но другой не должен AFAIK, это просто клиентский код для вызова других HTTP-сервисов.
Таким образом, вы почти всегда будете привязываться к порту (если только вы не выполняете какие-либо действия уровня <3), а для HTTP это обычно порт 80. Если вы не настроили эти две службы для использования разных портов, то это это было бы первое, что я бы отрегулировал.
Извините, если я не ясно выразился, но только один поток является сервером (порт, к которому он привязан, я контролирую). Другой - это просто клиентский код AFAIK, но, возможно, я что-то упускаю.
Ага, нет, это моя собственная оплошность в отношении «клиента». Первый поток BLOCKED
находится внутри статического инициализатора InetAddress
, поэтому любой поток, который попытается использовать InetAddress
, также станет «заблокированным». Вам действительно нужно будет решить первую цепочку BLOCKED
этого #loadLibrary
звонка.
Да, это действительно похоже на тупик. Вы можете попытаться решить эту проблему, используя InetAddress
в начале вашего приложения (до запуска другого кода - особенно перед использованием чего-либо из Netty).
Это исправлено в репозитории 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.
Ошибка исправлена Нетти: github.com/netty/netty/pull/13879 🎉
Приятно слышать.
Они связывают разные порты? Возможно, один сервер заблокирован в ожидании, пока другой сервер освободит привязку адреса.