文档章节

dubbo多网卡

Mr_Qi
 Mr_Qi
发布于 07/16 13:13
字数 1414
阅读 118
收藏 1

背景

今天小伙伴找我看一个现象。某个环境的dubbo无法掉通 但是通过直连的方式可以调用。Dubbo配置直连

并且代码没有改过。且给出的前提是该provider在局域网环境完全可以正常消费

简单描述就是

注册中心:zookeeper

消费者和生产者在两个不同的局域网 通过公网上的zookeeper进行注册【测试】===》这种实践很不好 破坏了一致性的前提条件

经过上述的场景分析 初步确定可能存在几种可能

  1. group变化
  2. provider中断【比如网络发生了变化关闭了端口】
  3. 网络异常导致服务者使用的是局域网地址而不是公网地址
  4. provider没有注册到zookeeper上

分析

首先由于第一步通过telnet进行了provider的可用性测试 Dubbo之telnet实现

通过telnet完成了校验 该provider可以成功调用。那么可以排除端口被关闭的可能。

其次通过zookeeper的四字指令校验了确实存在对应的consumer和provider 同时

也确认了provider的group和consumer的group一致

但是provider的地址变成了局域网地址。

基本可以确定局域网地址导致了不同的网络通信无法成功。

举例

zookeeper可以认为是个小区 而对应provider上的地址表示是一个门牌号。

当都处于局域网环境【即provider和consumer在同一个小区】我们用3幢101即可以定位到对应的provider而完成通信。

结果某一天zookeeper同时开始容纳了多个小区【多个局域网】

隔壁小区的地址同样写了3幢101 此时可能就无法找到正确的地址。

因此此时我们必须通过公网ip进行通信【可能还涉及到Nat】

源码

由于我们使用dubbo的时候并未指定注册的ip或者域名 那么其实dubbo是如何完成注册的呢???

在Protocol中可以注册host【一般针对多网卡】

当在Service决定暴露出来的时候如下

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    String name = protocolConfig.getName();
    if (name == null || name.length() == 0) {
        name = "dubbo";
    }
 
    String host = protocolConfig.getHost();
    if (provider != null && (host == null || host.length() == 0)) {
        host = provider.getHost();
    }
    boolean anyhost = false;
    if (NetUtils.isInvalidLocalHost(host)) {
        anyhost = true;
        try {
            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 {
                            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);
                    }
                }
            }
            if (NetUtils.isInvalidLocalHost(host)) {
                host = NetUtils.getLocalHost();
            }
        }
    }
}

从这边可以看出当没有设置host的时候我们会调用 host = InetAddress.getLocalHost().getHostAddress();

/**
 * Returns the address of the local host. This is achieved by retrieving
 * the name of the host from the system, then resolving that name into
 * an <code>InetAddress</code>.
 *
 * <P>Note: The resolved address may be cached for a short period of time.
 * </P>
 *
 * <p>If there is a security manager, its
 * <code>checkConnect</code> method is called
 * with the local host name and <code>-1</code>
 * as its arguments to see if the operation is allowed.
 * If the operation is not allowed, an InetAddress representing
 * the loopback address is returned.
 *
 * @return     the address of the local host.
 *
 * @exception  UnknownHostException  if the local host name could not
 *             be resolved into an address.
 *
 * @see SecurityManager#checkConnect
 * @see java.net.InetAddress#getByName(java.lang.String)
 */
public static InetAddress getLocalHost() throws UnknownHostException {
 
    SecurityManager security = System.getSecurityManager();
    try {
        String local = impl.getLocalHostName();
 
        if (security != null) {
            security.checkConnect(local, -1);
        }
 
        if (local.equals("localhost")) {
            return impl.loopbackAddress();
        }
 
        InetAddress ret = null;
        synchronized (cacheLock) {
            long now = System.currentTimeMillis();
            if (cachedLocalHost != null) {
                if ((now - cacheTime) < maxCacheTime) // Less than 5s old?
                    ret = cachedLocalHost;
                else
                    cachedLocalHost = null;
            }
 
            // we are calling getAddressesFromNameService directly
            // to avoid getting localHost from cache
            if (ret == null) {
                InetAddress[] localAddrs;
                try {
                    localAddrs =
                        InetAddress.getAddressesFromNameService(local, null);
                } catch (UnknownHostException uhe) {
                    // Rethrow with a more informative error message.
                    UnknownHostException uhe2 =
                        new UnknownHostException(local + ": " +
                                                 uhe.getMessage());
                    uhe2.initCause(uhe);
                    throw uhe2;
                }
                cachedLocalHost = localAddrs[0];
                cacheTime = now;
                ret = localAddrs[0];
            }
        }
        return ret;
    } catch (java.lang.SecurityException e) {
        return impl.loopbackAddress();
    }
}

但是当取得的是回环地址或者一些不合法的比如localhost的时候会重新设置

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());
}

当取不到合适的ip地址的时候

if (NetUtils.isInvalidLocalHost(host)) {
        if (registryURLs != null && registryURLs.size() > 0) {
            for (URL registryURL : registryURLs) {
                try {
                    Socket socket = new Socket();
                    try {
                        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);
                }
            }
        }
        if (NetUtils.isInvalidLocalHost(host)) {
            host = NetUtils.getLocalHost();
        }
    }
}

dubbo会通过和注册中心建立连接之后获取本地的ip

如果此时和注册中心无法建立连接呢或者其他异常呢???

那么就会使用localhost

public static String getLocalHost(){
    InetAddress address = getLocalAddress();
    return address == null ? LOCALHOST : address.getHostAddress();
}
/**
 * 遍历本地网卡,返回第一个合理的IP。
 *
 * @return 本地网卡IP
 */
public static InetAddress getLocalAddress() {
    if (LOCAL_ADDRESS != null)
        return LOCAL_ADDRESS;
    InetAddress localAddress = getLocalAddress0();
    LOCAL_ADDRESS = localAddress;
    return localAddress;
}
private static InetAddress getLocalAddress0() {
    InetAddress localAddress = null;
    try {
        localAddress = InetAddress.getLocalHost();
        if (isValidAddress(localAddress)) {
            return localAddress;
        }
    } catch (Throwable e) {
        logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
    }
    try {
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        if (interfaces != null) {
            while (interfaces.hasMoreElements()) {
                try {
                    NetworkInterface network = interfaces.nextElement();
                    Enumeration<InetAddress> addresses = network.getInetAddresses();
                    if (addresses != null) {
                        while (addresses.hasMoreElements()) {
                            try {
                                InetAddress address = addresses.nextElement();
                                if (isValidAddress(address)) {
                                    return address;
                                }
                            } catch (Throwable e) {
                                logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
                            }
                        }
                    }
                } catch (Throwable e) {
                    logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
                }
            }
        }
    } catch (Throwable e) {
        logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
    }
    logger.error("Could not get local host ip address, will use 127.0.0.1 instead.");
    return localAddress;
}

因此想要同一个地址同时被内网和外网可以访问 那么需要在注册时指定host 该host为域名

这样内网的机器可以解析该域名为局域网地址 而外网机器可以解析域名为公网地址

当然公网同样涉及到Nat技术。因此归根及底dubbo的rpc调用或者zookeeper的注册机制对于系统间调用都是不合适的。

理当避免。

© 著作权归作者所有

共有 人打赏支持
下一篇: 代理!代理!
Mr_Qi

Mr_Qi

粉丝 280
博文 359
码字总数 369228
作品 0
南京
程序员
私信 提问
JFinal 中使用 Dubbo —— 3 集群

集群 1.1. 部署结构 下面是一个简单的Cunsumer端服务器和Provider端服务器分别集群的部署图: 在个人开发机上,实现Cunsumer端服务器集群难以实现,所以此Demo中只实现Provider端服务器集群,...

糊搞
2015/04/21
0
25
dubbo 双网卡或者VPN的时候,服务提供者的错误IP注册到中心

使用了VPN,启动了dubbo服务提供者应用,又连了正式环境的注册中心; 一旦dubbo获取的ip错误后(拨了vpn 本机IP就会有多个), 这种情况即使提供者服务停掉,目前dubbo没有能力清除这类错误的...

greki
2015/04/17
0
0
dubbo深入学习的一些总结

Dubbo 源文件主要包含以上这么多包,其中: 首先对dubbo-2.8.4.jar 源码的一个截图 dubbo-common 公共逻辑模块,包括 Util 类和通用模型。 dubbo-remoting 远程通讯模块,相当于 Dubbo 协议的...

JTA的阿呆
2016/12/19
56
0
dubbo提供者出现不明外网ip注册的问题

之前碰到过这样的问题,在dubbo admin里会看到有不明的外网IP,服务虽然是注册成功了,但是提供者确实来自不明的IP。 今天做新项目上线,又碰到了这种情况。灵机一动会不会是因为dubbo在获取...

bfleeee
2015/10/13
407
0
解决由于内外网导致的无法访问dubbo上面的服务

内网: 172.21.16.8 外网:140.143.38.68 在服务器部署了dubbo服务和 一个提供方 启动后dubbo看到的提供方的ip是172.21.16.8:20880 这样消费服务从其他服务器访问该dubbo服务,就无法调用,由...

Mr_Tea伯奕
04/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Ubuntu18.04 安装MySQL

1.安装MySQL sudo apt-get install mysql-server 2.配置MySQL sudo mysql_secure_installation 3.设置MySQL非root用户 设置原因:配置过程为系统root权限,在构建MySQL连接时出现错误:ERROR...

AI_SKI
今天
3
0
3.6 rc脚本(start方法) 3.7 rc脚本(stop和status方法) 3.8 rc脚本(以daemon方式启动)

3.6-3.7 rc脚本(start、stop和status方法) #!/usr/bin/env python# -*- coding: utf-8 -*-# [@Version](https://my.oschina.net/u/931210) : python 2.7# [@Time](https://my.oschina.......

隐匿的蚂蚁
今天
3
0
Cnn学习相关博客

CNN卷积神经网络原理讲解+图片识别应用(附源码) 笨方法学习CNN图像识别系列 深度学习图像识别项目(中):Keras和卷积神经网络(CNN) 卷积神经网络模型部署到移动设备 使用CNN神经网络进行...

-九天-
昨天
5
0
flutter 底部输入框 聊天输入框 Flexible

想在页面底部放个输入框,结果键盘一直遮住了,原来是布局问题 Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("评论"), ...

大灰狼wow
昨天
4
0
Kernel I2C子系统

备注:所有图片来源于网络 1,I2C协议: 物理拓扑: I2C总线由两根信号线组成,一条是时钟信号线SCL,一条是数据信号线SDA。一条I2C总线可以接多个设备,每个设备都接入I2C总线的SCL和SDA。I...

yepanl
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部