InetAddress.getLocalHost 执行很慢?

背景介绍某次在 SpringBoot 2.2.0 项目的一个配置类中引入了这么一行代码:
InetAddress.getLocalHost().getHostAddress()导致项目启动明显变慢 。同时报出了相关的警告信息:

2022-10-03 23:32:01.806 [TID: N/A] WARN [main] o.s.b.StartupInfoLogger - InetAddress.getLocalHost().getHostName() took 5007 milliseconds to respond. Please verify your network configuration (macOS machines may need to add entries to /etc/hosts).

InetAddress.getLocalHost 执行很慢?

文章插图
根据报警信息可知,只要获取主机信息的耗时超过了阈值HOST_NAME_RESOLVE_THRESHOLD=200ms,就会提示这个信息 。很明显,我们的耗时已经超过5s 。同时,如果为 Mac 系统,还会贴心地提示在/etc/hosts文件中配置本地dns 。
我们看看目前hosts文件中的配置:
127.0.0.1 localhost255.255.255.255 broadcasthost::1localhost根据网上各种文章的提示,我们将主机名追加进去,变成这样:
127.0.0.1 localhost xiaoxi666s-MacBook-Pro.local255.255.255.255 broadcasthost::1localhost其中,xiaoxi666s-MacBook-Pro.local 就是我的主机名 。
注:更改hosts文件内容后,可使用命令 sudo killall -HUP mDNSResponder 刷新dns,无需重启电脑 。
再次启动 SpringBoot 程序,我们发现警告信息消失了,也就意味着主机信息获取的耗时不会超过200ms 。
那么问题来了,这背后究竟是什么机制,让我们一探究竟 。
使用Wireshark抓包看看由于我们要获取自己的主机信息,这里走的是本地回环网络,因此选中Loopback网络接口:
InetAddress.getLocalHost 执行很慢?

文章插图
先把hosts改回去,抓一下hosts文件改动前的网络包:
InetAddress.getLocalHost 执行很慢?

文章插图
按照时间顺序,可以将抓到的网络包分为三段,每段中又可以分为Ipv4和Ipv6两种地址的请求 。
其中用到的协议是 mdns,也即多播dns(Multicast DNS),它主要实现了在没有传统 dns 服务器的情况下使局域网内的主机实现相互发现和通信,使用的端口为 5353,遵从 dns 协议 。随便点开一个请求查看详情便可以得到验证:
InetAddress.getLocalHost 执行很慢?

文章插图
另外,网络包中的目标ip 224.0.0.251是 Mac 的官方 mdns 查询地址,详情可参见https://github.com/apple-oss-distributions/mDNSResponder/tree/mDNSResponder-1096.100.3
实际多次测试发现 , 主机信息都在第三次发送网络包后返回(阻塞在 InetAddress.getLocalHost() 方法上 。参见下图,阻塞在第18行,5秒后才跳到第19行) 。从上图的时间线看,约在8秒时返回,整体耗时与上面报出的 5007ms 吻合 。再仔细观察网络包,看起来是连续发了三次请求 。第一次在 3.1s 时发出,第二次在 4.1s 时发出,第三次在 7.1s 时发出,重试间隔分别为 1s 和 3s,看起来像是一种指数退避的重试 。当然,8秒左右时返回结果,就对应第一次请求,剩下两次请求的结果被忽略了 。
InetAddress.getLocalHost 执行很慢?

文章插图
我们再看看hosts中添加主机信息后,对应的网络包:
InetAddress.getLocalHost 执行很慢?

文章插图
啊噢,这次没有抓到任何相关的网络包,猜测直接读取了hosts文件拿到了主机名,根本没走网络 。
那么,这段获取主机信息的程序究竟是怎么运作的呢,hosts文件中没有添加主机名时 , 时间都耗在了哪里?
看看对应的源码源码比较好找 , 参见下图:
InetAddress.getLocalHost 执行很慢?

文章插图
我们再次把hosts中的主机名去掉,并使用 Arthas 工具的 trace 命令看看链路耗时:
InetAddress.getLocalHost 执行很慢?

文章插图
提示:如果抓包时出现 No class or method is affected 的报错,可查看对应的日志文件进行排查,见下图:
InetAddress.getLocalHost 执行很慢?

文章插图
InetAddress.getLocalHost 执行很慢?

文章插图
可知需要提升下权限 , 执行命令 options unsafe true 后,再尝试使用 trace命令即可 。
但好巧不巧,居然抓不到调用链?那我们试试用 Arthas 的 profiler 命令生成一下火焰图吧:
InetAddress.getLocalHost 执行很慢?

推荐阅读