kaka的gravatar头像
kaka2017-12-01 18:55:30
dubbo注册服务解析IP异常问题解决方式

最近项目在HA环境下,dubbo注册服务解析出问题了,dubbo目前都是单机的,比如服务器A吧,配置的物理IP地址是192.168.211.218,但dubbo解析出来的ip地址是192.168.211.215(某个虚拟IP地址),如下:

dubbo注册服务解析IP异常问题解决方式

这台机器上配置了很多歌虚拟IP,用于HA环境中通过keepalived去切换比如mysql、memcached等服务。这样错误的解析结果导致service一直无法被访问到,调用方一直在报错。

造成这种结果的原因是什么呢?可以先看下dubbo解析IP地址的源码,我截取其中关键的片段如下:

ServiceConfig类下的doExportUrlsFor1Protocol方法

 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
      ...................省略......................

        //1.先从ProtocolConfig中获取host
        String host = protocolConfig.getHost();
        //2.如果host为空,再从ProviderConfig中获取host
        if (provider != null && (host == null || host.length() == 0)) {
            host = provider.getHost();
        }
        boolean anyhost = false;
        //3.验证host是否为本地可用的host,如果是本地host(0.0.0.0或者localhost或者127.0.0.1之类)则继续解析
        if (NetUtils.isInvalidLocalHost(host)) {
            anyhost = true;
            try {
                //4.通过InetAddress的方式获取host,默认读取本机hosts中hostname对应的ip地址
                host = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                logger.warn(e.getMessage(), e);
            }
            if (NetUtils.isInvalidLocalHost(host)) {
                if (registryURLs != null && registryURLs.size() > 0) {
                    for (URL registryURL : registryURLs) {
                        try {
                            Socket socket = new Socket();
                            try {
                                //5.通过SocketAddress的方式获取host,一般情况下解析到此处就可以得到正确的本地ip,但是因为我配置了很多虚拟ip,所以这里导致了解析异常
                                SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                                socket.connect(addr, 1000);
                                host = socket.getLocalAddress().getHostAddress();
                                break;
                            } finally {
                                try {
                                    socket.close();
                                } catch (Throwable e) {}
                            }
                        } catch (Exception e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }
                }
                //6.遍历本地网卡,返回一个合理的host 
                if (NetUtils.isInvalidLocalHost(host)) {
                    host = NetUtils.getLocalHost();
                }
            }
        }
........................省略................................
}

验证是否为本地可用IP地址的方法

 public static boolean isInvalidLocalHost(String host) {
        return host == null 
        			|| host.length() == 0
                    || host.equalsIgnoreCase("localhost")
                    || host.equals("0.0.0.0")
                    || (LOCAL_IP_PATTERN.matcher(host).matches());
    }

其他的源码有兴趣大家自己点进去看下,我这里大概分析下原因:

dubbo解析IP地址的步骤如下:

1. 先从ProtocolConfig中取host
2. 再从ProviderConfig中取host
3. 若取出的是本地host, 则继续取host
4. 通过InetAddress.getLocalHost().getHostAddress();的方式获取Host
5. 通过Socket的方式尝试连接到注册中心,通过socket.getLocalAddress().getHostAddress()获取Host
6. 遍历本地网卡, 返回第一个合理的Host
第4步,就是取hostname,然后通过DNS把hostname解析成对应的IP,在我HA环境中,hostname对应的ip地址是127.0.0.1,如下:

dubbo注册服务解析IP异常问题解决方式

dubbo解析到ip为127.0.0.1后他会放弃使用,继续使用后面的解析方式,他会再尝试连接zookeeper,然后用socket返回的本地地址,这个地址就是socket自己选择的了,所以他选择了一个虚拟IP地址。为什么在单机环境中没有出现这个问题呢,因为单机环境没有配置虚拟IP,即使dubbo根据hostname解析不到具体的IP,使用socket方式依然能够拿到正确的ip地址。

解决方法:

配置本机主机名,在hosts中加入本机名和本机物理ip的映射关系


打赏

已有1人打赏

最代码官方的gravatar头像

分享到:

最近浏览
zqytnn LV17月19日
星星
wangvvvvx7月18日
暂无贡献等级
920112510 LV14月17日
星星
简单3214月5日
暂无贡献等级
xinxlahu LV43月23日
月亮
jibuzhu287 LV33月9日
星星星星星星
Jasonliu123 LV62月6日
月亮星星星星
zyl LV341月8日
太阳太阳星星星星
huo1993 LV81月6日
月亮月亮
zhos0212 LV162017年12月26日
太阳
顶部客服微信二维码底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友