InetAddress.getLocalHost 执行很慢?( 三 )

# hosts 文件中添加主机名times=1789ip addresss: 127.0.0.1可以看到,当 hosts 文件中没有添加主机名时,根本找不到对应的网络地址(因为 dns 中也没有解析到),添加之后就能返回对应的 ip 127.0.0.1 了 。
这里有几个地方需要注意:

  1. 即使 hosts 文件中添加主机名,标准 Linux 的 getaddrinfo 方法执行时,也会有接近两秒的耗时,但我们在 Java 代码中运行时却只有几十毫秒;
  2. 前文我们使用 Wireshark 抓包时提到,mdns 查询时存在重试机制,但标准 Linux 的 getaddrinfo 方法中没有看到对应的代码;
  3. 前面提到的5秒返回结果,其实不是返回结果,而是超时了 。但标准 Linux 的 getaddrinfo 方法中没有看到对应的超时控制代码;
因此,我们可以大胆猜测 MaxOS 系统对标准 Linux 代码进行了修改,加了本地缓存、重试、超时等机制 。
接着上面的第3点,回到 Java 项目调试一下,看看为什么超时了还能返回结果 。
当 hosts 文件中没有添加主机名时,会返回本机所有的 ip 地址:
InetAddress.getLocalHost 执行很慢?

文章插图
当 hosts 文件中添加主机名后,只会返回配置的 127.0.01 的 ip 地址:
InetAddress.getLocalHost 执行很慢?

文章插图
其中,当 hosts 文件中没有添加主机名时,getaddrinfo 调用返回错误码,此时 jdk 会转而调用 lookupIfLocalhost 方法,它内部调用了操作系统的 getifaddrs 方法,以获取本机所有 ip 地址:
InetAddress.getLocalHost 执行很慢?

文章插图

InetAddress.getLocalHost 执行很慢?

文章插图
对应的源码可以参考https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/ifaddrs.c.html 。
总结本文以 Java 中获取主机名慢的场景为契机,使用多种技术手段研究背后的原理,包括使用 Wireshark 抓包,使用 Arthas 工具定位到性能瓶颈,再转到 jdk 中查看对应的 native 方法实现,由于没找到最底层调用链路源码,转而参照标准Linux的相关源码,简单复现了上述场景 。
进一步地,由于没找到最底层调用链路源码,我们根据现象猜测的本地缓存、重试、超时等机制没有得到验证,有兴趣的同学可以进一步研究探索 。
参考文章
如何查找 jdk 中的 native 实现
从Chrome源码看DNS解析过程
getaddrinfo工作原理分析
浅谈getaddrinfo函数的超时处理机制
【InetAddress.getLocalHost 执行很慢?】

经验总结扩展阅读