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头像

分享到:

最近浏览
zoubo123112月11日
最代码贡献等级说明
水心木支12月8日
最代码贡献等级说明
feiyun31512月6日
暂无贡献等级
luo90090312月6日
最代码贡献等级说明
zccmp2012月6日
最代码贡献等级说明
1371639686112月6日
暂无贡献等级
gottaBoy12月5日
暂无贡献等级
aihui52312月5日
最代码贡献等级说明
Magic丶M12月5日
最代码贡献等级说明
程序猿全敏 LV312月4日
最代码贡献等级说明
tjthink12月4日
最代码贡献等级说明
uuu121012月4日
最代码贡献等级说明
cp1979110112月3日
暂无贡献等级
weiguang112月2日
暂无贡献等级
kaka LV312月2日
最代码贡献等级说明
cwb635712312月2日
最代码贡献等级说明
最代码官方 LV412月1日
最代码贡献等级说明
顶部客服微信二维码底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友