使用 java.net.InetAddress.getLocalHost 查找主机名时线程进入阻塞状态

Sha*_*ath 2 java performance https inetaddress tomcat9

请在下面找到出现问题的应用程序/环境详细信息。

  • Java Web 应用程序部署在 Tomcat 9.0.35 上,JRE 版本为 1.8.0_231-b11
  • 该应用程序在部署在 Open shift Kubernetes 分发平台上的 docker 容器中运行。

我看到应用程序中的许多线程有时会进入阻塞状态几分钟。在线程转储分析中,发现 java.net.InetAddress.getLocalHost 调用花费了太多时间。很多线程都卡在这里。为应用程序中打印的每个记录器获取主机名。

该问题是间歇性的。但是当它发生时,应用程序/tomcat将进入暂停状态,从而导致大量线程的积累。一段时间(几秒钟)后,所有阻塞的线程同时解除阻塞。由于请求并发,应用程序将耗尽它在池中维护的数据库连接,从而导致问题/缓慢/服务可用性。作为修复,我确保仅将主机名访问一次到静态变量中,并在整个日志记录过程中使用相同的主机名。我想知道这个问题的详细根本原因。

  • 为什么这个问题会间歇性地出现?
  • 这个kubernetes环境下DNS查找有问题吗?
  • 我们正在使用 IPV4 协议/地址
  • 有没有更好的方法/修复来处理这个问题?

以下来自线程转储的示例:

 "https-jsse-nio-8443-exec-13" #95 daemon prio=5 os_prio=0 tid=0x00007fccadbba800 nid=0xaf5 waiting for monitor entry 0x00007fcb912d1000
       java.lang.Thread.State: BLOCKED (on object monitor)
        at java.net.InetAddress.getLocalHost(InetAddress.java:1486)
        - waiting to lock <0x00000005e71878a0> (a java.lang.Object)
Run Code Online (Sandbox Code Playgroud)

apa*_*gin 5

在 JDK 8 中,InetAddress.getLocalHost()工作原理如下

  1. 通过本机gethostname调用以字符串形式获取主机名。
  2. 如果距离上次主机名解析不到 5 秒,则返回缓存的 IP 地址。
  3. 否则解析主机名:
    • 使用JDK内置的查找缓存,其默认TTL等于30秒;
    • 使用系统调用,该调用执行实际的 DNS 查找(根据配置,操作系统和 DNS 服务器可能会进一步缓存该地址)。
  4. 将解析后的本地主机IP地址缓存5秒。

步骤2-4在全局下执行cacheLock。如果在此过程中出现问题,所有调用的线程都InetAddress.getLocalHost()将在此锁处阻塞——正如您所观察到的那样。

通常,只要主机地址硬编码在/etc/hosts. 但在你的情况下,似乎涉及真正的网络请求(每当 TTL 过期时)。而当第一个 DNS 请求超时时(UDP 毕竟不是一个可靠的协议),就会发生延迟。

解决方案是配置/etc/hosts包含本地主机的名称和地址,例如

192.168.1.23   myhost.mydomain
Run Code Online (Sandbox Code Playgroud)

其中myhost.mydomain与命令返回的字符串相同hostname

最后,如果预计主机名在应用程序运行时不会更改,那么在应用程序级别上永久缓存它似乎是一个很好的解决方案。