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 執行很慢?

推薦閱讀