dubbo 错误ip及ip乱入的问题解决-InetAddress还是DNS的锅

一、简述问题背景

同事在本地启动dubbo服务时,发现自己的注册到zk上的dubbo 地址为:
dubbo://192.168.20.100:20880/com.xxx.xxx...

而其实际的ip 地址是192.168.10.60,他换了好几次不同的版本号启动应用,发现始终不是正常的ip地址,于是,开始了这次排插过程~~,看解决方案可以直接到三。


二、查看源码

1.首先,我们看下同事自身配置的dubbo.xml,里面的 dubbo:provider和dubbo.protocol均无强制指定的URL,排除是自身dubbo配置文件的问题
2.我们开始看dubbo 里面是怎么初始化提供者的url,由于dubbo 中存在ServiceBean实现了InitializingBean接口,因此在Spring示例化这个bean后会调用里面的 afterPropertiesSet 方法:

public void afterPropertiesSet() throws Exception {
    //前面省略部分代码,这些代码是在判断配置项缺失时的处理
    ......
    // 处理完后暴露provider
    if (!isDelay()) {
        export();
    }
}

3.接着我们到export方法里面看,它做了什么,它在 ServiceConfig 中:

    public synchronized void export() {
        // 这个代码段很好理解,就是 赋值 export和delay
        if (provider != null) {
            if (export == null) {
                export = provider.getExport();
            }
            if (delay == null) {
                delay = provider.getDelay();
            }
        }
        //如果不需要暴露接口,则不进行后续处理
        if (export != null && ! export.booleanValue()) {
            return;
        }
        //是否有 延时暴露?有的话 就sleep下再暴露
        if (delay != null && delay > 0) {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(delay);
                    } catch (Throwable e) {
                    }
                    //重点的暴露的方法
                    doExport();
                }
            });
            thread.setDaemon(true);
            thread.setName("DelayExportServiceThread");
            thread.start();
        } else {
            //重点的暴露的方法
            doExport();
        }
    }

4.我们着重看下doExport方法:

protected synchronized void doExport() {
    //由于代码过长,省略判断和检查部分代码,有兴趣可以到参考文档里面进行查看
    ......
    //检查每个配置,进去看了下,没有初始化host地址
    checkApplication();
    checkRegistry();
    checkProtocol();
    //关联 到provider
    appendProperties(this);
    checkStubAndMock(interfaceClass);
    if (path == null || path.length() == 0) {
          path = interfaceName;
    }
    //重点的 暴露urls
    doExportUrls();
}

5.doExportUrls:

private void doExportUrls() {
        List registryURLs = loadRegistries(true);
        for (ProtocolConfig protocolConfig : protocols){
        //我们直接看这一行
       doExportUrlsFor1Protocol(protocolConfig,registryURLs);
        }
 }

6.doExportUrlsFor1Protocol

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List registryURLs) {
    //省略不关键的代码
    ......
    // 从protocol的配置项和provider中获得host,我们没有配置,自然获得不到
    String host = protocolConfig.getHost();
    if (provider != null && (host == null || host.length() == 0)) {
            host = provider.getHost();
    }
    boolean anyhost = false;
    ......
    //当host 为null或非法或localost等符合dubbo NetUtil限制的时候
    try{
    // 第一步、这是获取host 的重点
         host = InetAddress.getLocalHost().getHostAddress();
    } catch (UnknownHostException e) {
       logger.warn(e.getMessage(), e);
    }
    ......
    //若第一步获取的host 还是不符合规则的,则对registryURLs进行操作处理,这个本次不讲
    ......
}

7.InetAddress这个里面在搞什么的呢?

public static InetAddress getLocalHost() throws UnknownHostException {
    ......
    //省略其他非重点,我们看这一行,根据name获得address
    try {
        localAddrs = InetAddress.getAddressesFromNameService(local, null);
    }
    ......
}

我们接着进去看

private static InetAddress[] getAddressesFromNameService(String host, InetAddress reqAddr) throws UnknownHostException{
    ......
    //此处获得 addresses
    addresses = nameService.lookupAllHostAddr(host);
    ......
}

8.一般来说,我们都会马上到这个实现类里面去,可怎么看都发现断点进不来,什么原因呢?在这个方法之前,createNSProvider 已经被执行了,里面对nameService 的方法进行了重写

private static NameService createNSProvider(String provider) {
    ......
    //此处new
nameService = new NameService() {
    public InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException{
        return impl.lookupAllHostAddr(host);
    }
   public String getHostByAddr(byte[] addr) throws UnknownHostException {
        return impl.getHostByAddr(addr);
   }
  };
  ......
}

lookupAllHostAddr 被Inet6AddressImpl和Inet4AddressImpl 实现,因此返回是ipv4和ipv6的地址。


三、分析及解决方案

分析:
- 而其ipv4返回的是错误的ip,且传入的host 名字很大众(”admins-MacBook-pro”),我们怀疑是否是DNS 解析的问题,相同的host 名称在同个局域网,解析到别的机子上的ip。

解决方案:
我们修改了 /etc/resolv.conf DNS 的配置,改成了

//重要
nameserver 8.8.8.8
即正常使用了

四、拓展-紧急处理的方式

  1. ip乱入,可以使用dubbo的强制指定host来解决,不过是一时之爽:
在/etc/hosts中加入机器名 公网IP,比如:
admins-MacBook-pro 192.168.10.60
或者 在dubbo.xml中加入主机地址的配置
"192.168.10.60"/>
或者在每个provider中绑定host
"192.168.10.60"/>
或者在JVM中指定host
-Ddubbo.properties.file=xxx.properties
然后 此properties中写入 host
  1. 看网上网友说,此问题的产生也有可能是 VPN导致本机IP多个,关闭VPN即可(ps:没遇到过,不知道对不对)

五、参考文档

http://dubbo.io/User+Guide-zh.htm
http://blog.csdn.net/flashflight/article/details/44473617
http://blog.csdn.net/lele2426/article/details/39530409#4483369
http://www.cnblogs.com/leo-li-3046/p/5702479.html

你可能感兴趣的:(dubbo)