主要问题:Nacos注册时,客户端多张网卡,IP选择造成的问题。
F12开发者工具发现访问我的服务时指向的IP为
192.168.142.1,再通过检测本机网卡列表发现其为
VMware Network Adapter VMnet8虚拟机的IP。最后通过在配置文件指定
spring.cloud.nacos.discovery.ip解决到该问题。以下信息为问题发生时的各种信息及个人粗浅理解nacos客户端注册IP的选择逻辑。
问题发生的情况
{
"code": 1,
"msg": "finishConnect(..) failed: Connection timed out: /192.168.142.1:3023",
"data": null
}
本机注册信息
2023-05-02 13:52:47.375 INFO 17964 --- [ main] c.a.c.n.registry.NacosServiceRegistry : nacos registry, DEFAULT_GROUP cloud-asset-management-biz 192.168.142.1:5017 register finished
本机网卡信息
Windows IP 配置
以太网适配器 以太网 2:
媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . :
以太网适配器 以太网:
连接特定的 DNS 后缀 . . . . . . . : vlan10
IPv4 地址 . . . . . . . . . . . . : 192.168.100.183
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 192.168.10.1
以太网适配器 以太网 3:
媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . :
以太网适配器 VMware Network Adapter VMnet1:
连接特定的 DNS 后缀 . . . . . . . :
IPv4 地址 . . . . . . . . . . . . : 192.168.192.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
以太网适配器 VMware Network Adapter VMnet8:
连接特定的 DNS 后缀 . . . . . . . :
IPv4 地址 . . . . . . . . . . . . : 192.168.142.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
个人理解
namingService.registerInstance(serviceName, "192.168.1.100", port);
如果手动指定了IP地址,则使用该地址进行服务注册和服务发现。 如果配置文件中指定了IP地址,则使用该地址进行服务注册和服务发现。 否则,Nacos客户端会遍历所有的本地IP地址,选择一个可用的地址进行注册和发现。选择的条件包括:
IP地址必须为IPv4格式; IP地址不能是回环地址(127.0.0.1)或本地连接地址(169.254.X.X); IP地址必须是可达的(即不在黑名单中)。
Nacos客户端IP自动选择
public class UtilsAndCommons {
// 根据 IP 地址排序
@SuppressWarnings("unchecked")
public static List<String> getIPList(String ipList) {
String ipListString = IP_SPLIT_PATTERN.matcher(ipList).replaceAll(",");
List<String> ips = new ArrayList(Arrays.asList(ipListString.split(",")));
Collections.sort(ips, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
String[] arr1 = o1.split("\\.");
String[] arr2 = o2.split("\\.");
for (int i = 0; i < 4; i++) {
int seg1 = Integer.parseInt(arr1[i]);
int seg2 = Integer.parseInt(arr2[i]);
if (seg1 != seg2) {
return seg1 - seg2;
}
}
return 0;
}
});
return ips;
}
// 获取本地 IP 地址列表
public static List<String> getLocalIPList() {
List<String> result = new ArrayList<String>();
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
if (ni.isLoopback()) {
continue;
}
Enumeration<InetAddress> addresses = ni.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
if (addr.isLoopbackAddress() || !(addr instanceof Inet4Address)) {
continue;
}
result.add(addr.getHostAddress());
}
}
} catch (SocketException e) {
Loggers.SRV_LOG.error("get local ip list error!" + e.getMessage(), e);
}
return result;
}
// 获取服务器 IP 地址列表
public static List<String> getServerIps() {
List<String> ips = new ArrayList<>();
try {
Enumeration<NetworkInterface> nics = NetworkInterface.getNetworkInterfaces();
LOOP:
while (nics.hasMoreElements()) {
NetworkInterface nic = nics.nextElement();
if (nic.isLoopback() || nic.isVirtual() || !nic.isUp()) {
continue;
}
Enumeration<InetAddress> addresses = nic.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
if (addr.isLoopbackAddress() || !(addr instanceof Inet4Address)) {
continue;
}
ips.add(addr.getHostAddress());
if (NetUtils.canResolved(addr.getHostName())) {
ips.add(addr.getHostName());
}
}
}
} catch (SocketException e) {
Loggers.SRV_LOG.warn("failed to retrieve the serverIp! {}", e.getMessage());
}
return ips;
}
// 根据 IP 地址获取网段
public static String getIPSegment(String ipAddr) {
String[] arr = ipAddr.split("\\.");
if (arr.length != 4) {
return "";
}
return arr[0] + "." + arr[1] + "." + arr[2];
}
}
核心代码主要包括:
getIPList
方法:根据 IP 地址排序。getLocalIPList
方法:获取本地 IP 地址列表。getServerIps
方法:获取服务器 IP 地址列表。getIPSegment
方法:根据 IP 地址获取网段。
-Dnacos.bind.ip参数,如果没有配置该参数,则根据触发条件选择 IP 地址。具体流程如下:
如果 nacos-client 与 nacos-server 在同一台机器上运行,则优先选择本地 IP 地址,即从 getLocalIPList
方法返回的 IP 地址列表中选择一个 IP 地址。如果 nacos-client 与 nacos-server 不在同一台机器上运行,则选择非本地 IP 地址。选择的规则为:假设 nacos-client 获取到的本地 IP 地址为 IP1,nacos-server 获取到的本地 IP 地址为 IP2,则选择 IP2 所在的网段中距离 IP1 最近(按照 IP 地址排序)的一个 IP 地址。如果不存在则选择 IP2。 如果没有选择到合适的 IP 地址,则会使用 0.0.0.0 地址。
1. Nacos官方文档:https://nacos.io/zh-cn/docs/quick-start.html
2. Nacos源码 https://github.com/alibaba/nacos
文章转载自左羊公社,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




