kaka的gravatar头像
kaka 2017-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头像
最近浏览
sean_ss  LV1 2021年8月12日
陈石养  LV2 2021年7月5日
qazqaz32  LV5 2021年3月27日
liuhao8571 2020年10月19日
暂无贡献等级
eko  LV7 2020年2月11日
懒狐狸  LV1 2019年12月9日
tangyao  LV8 2019年4月10日
kikock  LV2 2019年4月1日
RichardDai  LV2 2019年3月27日
浪子逍遥遥  LV18 2019年1月21日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友