spring-boot-admin无法连接客户端侧的错误分析

2,362 阅读2分钟

在使用win10系统开发中,当我们不使用注册中心部署 spring-boot-admin 和相对应的客户端时,可能会出现 admin-server 无法拉取客户端侧监控信息的问题,解决方案很简单,只需要在客户端测添加以下一行配置就行:

spring.boot.admin.client.instance.prefer-ip: true # 使用ip注册

分析原因

当启动客户端后,我们发现 server 端出现以下错误

failed to resolve 'xxx.mshome.net' after 4 queries ; nested exception is java.net.UnknownHostException: failed to resolve 'LAPTOP-CDQ9PAID.mshome.net' after 4 queries

看来是 xxx.mshome.net 导致的一个问题,我们查询本机的 dns 缓存,发现了以下一段记录:

    1.224.17.172.in-addr.arpa
    ----------------------------------------
    记录名称. . . . . . . : 1.224.17.172.in-addr.arpa.
    记录类型. . . . . . . : 12
    生存时间. . . . . . . : 0
    数据长度. . . . . . . : 8
    部分. . . . . . . . . : 答案
    PTR 记录  . . . . . . : xxx.mshome.net

先解释两个域名:in-addr.arpa 和 mshome.net

in-addr.arpa

.arpa 是一个专门用于互联网基础设施的一个顶级域名。现在他有两个二级域名:

  • in-addr.arpa。IPv4 DNS 反向查询
  • ip6.arpa。IPv6 DNS 反向查询 DNS域名解析是将域名解析出ip,而反向查询就是将ip解析成域名。

mshome.net

这个域名是微软的,当我们使用 windows网络连接共享(ICS)时,如果本机没有域名,系统会自动配置这个域名。

解决

spring-admin server 打印错误信息,无法处理 xxx.mshome.net 域名,我们深入了解一下处理过程。因为server与client是以心跳的方式交互的,我们只需要关注 client 是如何拿到这个域名的。
在 client 启动时,服务会自动注册一个 DefaultApplicationRegistrator,这个bean会调用 DefaultApplicationRegistrator#register 方法将 ApplicationFactory#createApplication创建的Application 注册到 server端,Application#managementUrl就是刚才这个 xxx.mshome.net 域名。 通过调用链,我们发现最终调用的是 InetAddress#getLocalHost,在这里会拿取 localAddrs[0],这个地址刚好是 xxx.mshome.net 代表的地址。
那么为什么配置了上面的参数就没问题了,主要是DefaultApplicationFactory#getHost这个方法:

protected String getHost(InetAddress address) {
		return this.instance.isPreferIp() ? address.getHostAddress() : address.getCanonicalHostName(); // 当prefer-ip=true时,使用ip而不是域名,这里是172.17.224.1
	}

再次探讨 mshome.net 怎么来的

ipconfig 打印网络配置

以太网适配器 vEthernet (WSL):

   连接特定的 DNS 后缀 . . . . . . . :
   本地链接 IPv6 地址. . . . . . . . : fe80::618e:d3dc:c21b:b510%68
   IPv4 地址 . . . . . . . . . . . . : 172.17.224.1
   子网掩码  . . . . . . . . . . . . : 255.255.240.0
   默认网关. . . . . . . . . . . . . :

发现这是 WSL 的网络!

总结

最好加上这个配置,以防出现未知错误

spring.boot.admin.client.instance.prefer-ip: true # 使用ip