Loading

Nacos 2.2.x版本客户端注册监听器时的多网卡问题

背景

在本地环境启动Nacos和SpringBoot应用测试Nacos的配置监听特性,Nacos控制台在本地以单机模式启动,SpringBoot应用在IDEA中启动;本地环境有一个VMware虚拟机实例。

问题

Nacos控制台绑定的IP为VMware虚拟机网卡的IP

Nacos控制台启动后绑定的IP为VMware虚拟网卡的IP:192.168.232.1
image
实际期望绑定到如下局域网唯一IP:
image

SpringBoot应用中的Nacos SDK注册到Nacos中的监听IP为VMware虚拟机网卡的IP

SpringBoot应用中引用Nacos-Client,然后使用ConfigService注册配置监听器,实际发现上报的客户端IP如下(虽然VMware虚拟网卡的ip也能正常监听配置变更):
image
同样期望绑定到192.168.166开头的局域网唯一IP。

解决方案

修改Nacos控制台配置以指定控制台IP

2.3.x版本及之前(实际测试有效):

nacos.inetutils.ip-address=你的ip

2.4.x版本及之后(官方文档给出的方案):

nacos.server.ip=你的ip

修改Nacos SDK注册监听器时上报到控制台的Client IP

Nacos SDK获取本地IP的逻辑如下:

package com.alibaba.nacos.api.utils;

public class NetUtils {
    
    private static final String CLIENT_LOCAL_IP_PROPERTY = "com.alibaba.nacos.client.local.ip";
    
    private static final String CLIENT_LOCAL_PREFER_HOSTNAME_PROPERTY = "com.alibaba.nacos.client.local.preferHostname";
    
    private static final String LEGAL_LOCAL_IP_PROPERTY = "java.net.preferIPv6Addresses";
    
    private static final String DEFAULT_SOLVE_FAILED_RETURN = "resolve_failed";
    
    private static String localIp;
    
    /**
     * Get local ip.
     *
     * @return local ip
     */
    public static String localIP() {
        if (!StringUtils.isEmpty(localIp)) {
            return localIp;
        }
        if (System.getProperties().containsKey(CLIENT_LOCAL_IP_PROPERTY)) {
            return localIp = System.getProperty(CLIENT_LOCAL_IP_PROPERTY, getAddress());
        }
        localIp = getAddress();
        return localIp;
    }
    
    private static String getAddress() {
        InetAddress inetAddress = findFirstNonLoopbackAddress();
        if (inetAddress == null) {
            return DEFAULT_SOLVE_FAILED_RETURN;
        }
        
        boolean preferHost = Boolean.parseBoolean(System.getProperty(CLIENT_LOCAL_PREFER_HOSTNAME_PROPERTY));
        return preferHost ? inetAddress.getHostName() : inetAddress.getHostAddress();
    }
    
    private static InetAddress findFirstNonLoopbackAddress() {
        InetAddress result = null;
        
        try {
            int lowest = Integer.MAX_VALUE;
            for (Enumeration<NetworkInterface> nics = NetworkInterface.getNetworkInterfaces();
                    nics.hasMoreElements(); ) {
                NetworkInterface ifc = nics.nextElement();
                if (ifc.isUp()) {
                    if (ifc.getIndex() < lowest || result == null) {
                        lowest = ifc.getIndex();
                    } else {
                        continue;
                    }
                    
                    for (Enumeration<InetAddress> addrs = ifc.getInetAddresses(); addrs.hasMoreElements(); ) {
                        InetAddress address = addrs.nextElement();
                        boolean isLegalIpVersion = Boolean.parseBoolean(System.getProperty(LEGAL_LOCAL_IP_PROPERTY))
                                ? address instanceof Inet6Address : address instanceof Inet4Address;
                        if (isLegalIpVersion && !address.isLoopbackAddress()) {
                            result = address;
                        }
                    }
                    
                }
            }
        } catch (IOException ex) {
            //ignore
        }
        
        if (result != null) {
            return result;
        }
        
        try {
            return InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            //ignore
        }
        
        return null;
        
    }
}

默认返回本机第一个非回环地址的ip,如果指定了com.alibaba.nacos.client.local.ip则使用指定的ip作为本地ip,所以只需要在启动命令加上如下部分即可

java -Dcom.alibaba.nacos.client.local.ip=你的IP app.jar

在IDEA中配置方法如下:
image

附录

Nacos SDK中监听器的使用注意事项

Nacos SDK中的监听器接口为com.alibaba.nacos.api.config.listener.Listener,其包含一个名为com.alibaba.nacos.client.config.listener.impl.AbstractConfigChangeListener的默认实现,但是这个实现默认仅支持对propertiesyaml两种类型的配置进行变更前后分析,其他类型的配置需要自己实现com.alibaba.nacos.api.config.listener.ConfigChangeParser接口并通过Nacos的SPI注册进去,部分源码如下:

package com.alibaba.nacos.client.config.impl;

public class ConfigChangeHandler {
    // ......
    private ConfigChangeHandler() {
        this.parserList = new LinkedList<>();

        Collection<ConfigChangeParser> loader = NacosServiceLoader.load(ConfigChangeParser.class);
        this.parserList.addAll(loader);

        this.parserList.add(new PropertiesChangeParser());
        this.parserList.add(new YmlChangeParser());
    }
	// ......
}

我这里要实现的监听js脚本文件变化并更新,只需要实现com.alibaba.nacos.api.config.listener.AbstractListener就行了。

posted @ 2025-03-31 12:08  天火33  阅读(210)  评论(0)    收藏  举报