您当前的位置:首页 > 电脑百科 > 程序开发 > 容器

记一次处理Dubbo多网卡注册IP错误问题

时间:2022-12-02 13:49:35  来源:  作者:Java热点

场景介绍

今天在Docker上部署服务时,启动都很成功,但是访问时却访问失败。之前在本地启动、在测试服k8s上启动都很正常,为什么同样的代码、同样的docker镜像在docker上却有问题呢?真让人摸不着头脑。

服务是部署在docker集群上,因为是部署测试,就准备了3台机器,由3台机器组成的一个集群。服务是按业务划分,分别写在不同的docker-compose.yml文件中,最后通过docker stack 启动。

dubbo的注册中心使用的是nacos。

因为在本地测试正常,而且也在k8s上部署且访问正常,所以在docker环境启动也很顺利。唯一不行的就是docker环境部署后部分接口访问报错。

过程排查

在接口访问失败后,立刻查看了服务日志,报错是dubbo接口调用超时。错误如下:

org.Apache.dubbo.rpc.RpcException: 
FAIled to invoke the method getExportedURLs in the service org.apache.dubbo.rpc.service.GenericService. 
Tried 1 times of the providers [172.18.0.3:20881] (1/1) from the registry 172.10.36.101:8848 on the consumer 172.19.0.7 using the dubbo version 2.7.17.
。。。。。省略一大堆。。。。。
error message is:Host is unreachable: /172.18.0.3:20881
复制代码

从报错日志来看,有个明显的错误是Host is unreachable。现在是服务C调用服务A的dubbo接口调用不到,用我蹩脚的英文理解刚才错误信息是主机不可达。

此时,有两个疑问出现在容量不够大的脑子里:

  1. 为什么服务C调用不了服务A?
  2. 172.18.0.3这个ip是什么东东,哪里来的。

针对问题1,首先想到的是服务A没有注册到nacos里去,打开nacos控制台发现服务A是已经注册上去了的,并且发现注册的ip是172.18.0.3,刚好和问题2里的ip一致。

 

既然服务已经正常注册,那就剩下172.18.0.3这个ip是哪里来的了。通过docker exec命令进入服务A容器,使用命名ifconfig看下服务A的ip信息:

 

找到了,是服务A容器的一个网卡地址,不过这个容器怎么网卡?难道是因为多个网卡导致的吗?那为什么部署在k8s容器里没有问题?难道k8s里面没有多个网卡吗?又接着一连串的问号在脑海里出现了?

先看看,k8s里的ip信息吧。通过kubectl exec登录容器, 查看ip信息ip addr。嗯?只有两个,比docker里少了好多。

 

看来是,dubbo注册的时候,选择网卡的时候,是有一定的机制的,选择的不是我想要的。看看源码吧,到底是怎么选择的。

源码分析

Dubbo获取网卡地址的逻辑是在
org.apache.dubbo.common.utils.NETUtils类中getLocalAddress0方法。

private static InetAddress getLocalAddress0() {
    InetAddress localAddress = null;

    // @since 2.7.6, choose the {@link NetworkInterface} first
    try {
        NetworkInterface networkInterface = findNetworkInterface();
        Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
        while (addresses.hasMoreElements()) {
            Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
            if (addressOp.isPresent()) {
                try {
                    if (addressOp.get().isReachable(100)) {
                        return addressOp.get();
                    }
                } catch (IOException e) {
                    // ignore
                }
            }
        }
    } catch (Throwable e) {
        logger.warn(e);
    }

    try {
        localAddress = InetAddress.getLocalHost();
        Optional<InetAddress> addressOp = toValidAddress(localAddress);
        if (addressOp.isPresent()) {
            return addressOp.get();
        }
    } catch (Throwable e) {
        logger.warn(e);
    }


    return localAddress;
}
复制代码

这块代码的整体逻辑还是很好理解的:

  1. 查找所有的网卡
  2. 校验网卡对应的ip是否合适
  3. 如果找不到合适的ip,则设置ip为127.0.0.1

再看下Dubbo是如何校验ip是否合适的呢?对应方法为toValidAddress

private static Optional<InetAddress> toValidAddress(InetAddress address) {
    if (address instanceof Inet6Address) {
        Inet6Address v6Address = (Inet6Address) address;
        if (isPreferIPV6Address()) {
            return Optional.ofNullable(normalizeV6Address(v6Address));
        }
    }
    if (isValidV4Address(address)) {
        return Optional.of(address);
    }
    return Optional.empty();
}
复制代码

先判断拿到的地址是否为ipv6,再看是否设置了优先选择ipv6,即有没有配置
JAVA.net.preferIPv6Addresses=true,我们项目配置的是java.net.preferIPv4Stack=true,所以走的是下面的逻辑。再检测是否是合法的ipv4地址,拿到ip后,检查ip的网速,如果响应时间为100ms内,则把这个ip作为注册ip。

知道了Dubbo是如何选择网卡的了,但是好像对我们没有太大帮助,我总不能限制网卡的网速去吧?

最好的办法还是,我是否可以设置?再看一遍源码是不是遗漏了什么。看下是如何选择网卡的:

public static NetworkInterface findNetworkInterface() {

    List<NetworkInterface> validNetworkInterfaces = emptyList();
    try {
        // 寻找合适的网卡
        validNetworkInterfaces = getValidNetworkInterfaces();
    } catch (Throwable e) {
        logger.warn(e);
    }

    NetworkInterface result = null;

    // Try to find the preferred one
    for (NetworkInterface networkInterface : validNetworkInterfaces) {
        // 是否为优选的网卡
        if (isPreferredNetworkInterface(networkInterface)) {
            result = networkInterface;
            break;
        }
    }

    if (result == null) { // If not found, try to get the first one
        for (NetworkInterface networkInterface : validNetworkInterfaces) {
            Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
            while (addresses.hasMoreElements()) {
                Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
                if (addressOp.isPresent()) {
                    try {
                        if (addressOp.get().isReachable(100)) {
                            return networkInterface;
                        }
                    } catch (IOException e) {
                        // ignore
                    }
                }
            }
        }
    }

    if (result == null) {
        result = first(validNetworkInterfaces);
    }

    return result;
}
复制代码

这方法也分为几步,也是很好理解的:

  1. 查找所有合适的网卡
  2. 在查找的网卡里遍历一遍,看是否有优先设置的
  3. 如果没有,选择一个网速100毫秒响应的
  4. 如果还没有,则返回第一个网卡

第一步,查找所有合适的网卡,怎么算合适的呢?getValidNetworkInterfaces方法里判断只要不是被忽略的网卡就是合适的,里面的代码就不细看了,大致逻辑是,通过参数
dubbo.network.interface.ignored可以设置哪些网卡被忽略,如果忽略多个可以用逗号拼接。例如dubbo.network.interface.ignored=eth0,eth1。这个参数好像可以满足我们的需求。

第二步,查找网卡是否有被我们优先设置的。
isPreferredNetworkInterface方法我们看下:

public static boolean isPreferredNetworkInterface(NetworkInterface networkInterface) {
    // dubbo.network.interface.preferred
    String preferredNetworkInterface = System.getProperty(DUBBO_PREFERRED_NETWORK_INTERFACE);
    return Objects.equals(networkInterface.getDisplayName(), preferredNetworkInterface);
}
复制代码

可以看到,我们可以通过
DUBBO_PREFERRED_NETWORK_INTERFACE这个参数,也就dubbo.network.interface.preferred来指定网卡。例如:dubbo.network.interface.preferred=eth0。

看到这里,我们就明白了,至少我们可以通过排除网卡或者设置网卡来让Dubbo选择合适的ip去注册。因为docker容器里,不一定有多少个确定的网卡,还是指定网卡比较保险

问题解决

好了,知道如何让Dubbo来选择网卡了,我们只要找到各个容器里在同一网段的网卡就好了。

于是,登录到各个docker容器里,查看ip信息。对比了一下,发现eth1这个ip的网段都是10.10.x.x网段的。

配置环境变量信息
dubbo.network.interface.preferred=eth1,重启服务,然后访问接口,果然通了。

大功告成!

后记

其实,还有其他办法来解决问题,比如:protocol配置host信息、设置 DUBBO_IP_TO_REGISTRY和DUBBO_IP_TO_BIND等。这里就不展开说明了,有兴趣的小伙伴可以自己试一试。



Tags:Dubbo   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
图解Dubbo,Dubbo 服务治理详解
当前,分布式服务在互联网行业中得到了广泛应用。然而,分布式服务不仅仅是将单个应用程序分割成不同的模块,还涉及到模块之间的相互合作和协作。服务治理是分布式服务的一个关键...【详细内容】
2023-10-17  Search: Dubbo  点击:(218)  评论:(0)  加入收藏
深入理解java和dubbo的SPI机制
作者 | 京东云开发者-京东物流 龚航林原文链接:https://my.oschina.net/u/4090830/blog/101160111 SPI 简介1.1 SPI(Service Provider Interface)本质:将接口实现类的全限定名...【详细内容】
2023-10-11  Search: Dubbo  点击:(244)  评论:(0)  加入收藏
实例讲解SpringBoot集成Dubbo的步骤及过程
首先,让我们先了解一下Spring Boot和Dubbo。Spring Boot 是一个开源的 Java Web 框架,它可以帮助开发者快速创建独立的、生产级别的 Spring 应用程序。Spring Boot 提供了很多...【详细内容】
2023-09-26  Search: Dubbo  点击:(251)  评论:(0)  加入收藏
Dubbo + Nacos这么玩就失去高可用的能力了
我们常用的微服务框架是SpringCloud那一套,在服务远程调用和注册中心的选型上也有不少方案。在服务远程调用上常用的有:Feign、Dubbo等,在注册中心上常用的有:Nacos、Zookeeper...【详细内容】
2023-09-08  Search: Dubbo  点击:(266)  评论:(0)  加入收藏
如何将 Dubbo Filter 拦截器原理运用到日志拦截器中?
业务背景我们希望可以在使用日志拦截器时,定义属于自己的拦截器方法。实现的方式有很多种,我们分别来看一下。拓展阅读java 注解结合 spring aop 实现自动输出日志[1]java 注...【详细内容】
2023-08-06  Search: Dubbo  点击:(227)  评论:(0)  加入收藏
基于Dubbo解决亿级流量中缓存双写策略问题
1.引言在处理大规模流量和高并发读写请求的分布式系统中,缓存双写是一项关键任务。保证缓存的一致性和高可用性是挑战性的,特别是在面对亿级流量的场景下。本文将探讨亿级流量...【详细内容】
2023-05-16  Search: Dubbo  点击:(348)  评论:(0)  加入收藏
图解Dubbo,六种扩展机制详解
今天详细的分解一下Dubbo的扩展机制,实现快速入门,丰富个人简历,提高面试level,给自己增加一点谈资,秒变面试小达人,BAT不是梦。说真的,从零学习Dubbo,看这个系列足够了,共10篇,欢迎持...【详细内容】
2023-04-12  Search: Dubbo  点击:(161)  评论:(0)  加入收藏
Go 语言体系下的微服务框架选型: Dubbo-go
作 者 | 牛学蔚(蔚俊)本文介绍了Go 微服务体系发展与选型,过去一年Dubbo-go 社区的飞速发展以及对未来的展望。一、Go 微服务体系发展与选型随着微服务技术的快速发展,其在各...【详细内容】
2023-04-11  Search: Dubbo  点击:(275)  评论:(0)  加入收藏
如何用一个端口同时暴露 HTTP1/2、gRPC、Dubbo 协议?
本文我们将介绍 Apache Dubbo 灵活的多协议设计原则,基于这一设计,在 Dubbo 框架底层可灵活的选用 HTTP/2、HTTP/REST、TCP、gRPC、JsonRPC、Hessian2 等任一 RPC 通信协议,同...【详细内容】
2023-03-17  Search: Dubbo  点击:(84)  评论:(0)  加入收藏
阿里一面:说一说Java、Spring、Dubbo三者SPI机制的原理和区别
大家好,我是三友~~今天来跟大家聊一聊Java、Spring、Dubbo三者SPI机制的原理和区别。其实我之前写过一篇类似的文章,但是这篇文章主要是剖析dubbo的SPI机制的源码,中间只是简单...【详细内容】
2023-03-14  Search: Dubbo  点击:(144)  评论:(0)  加入收藏
▌简易百科推荐
Docker 和传统虚拟机有什么区别?
我有一个程序员朋友,他每年情人节都要送女朋友一台服务器。他说:“谁不想在过节当天收到一台 4核8g 的服务器呢?”“万一对方不要,我还能留着自己用。” 给他一次过节的机会,他能...【详细内容】
2024-03-26  小白debug  微信公众号  Tags:Docker   点击:(12)  评论:(0)  加入收藏
掌握Docker网络驱动程序:优化容器通信
Docker为在容器内包装、交付和运行应用程序提供了一个强大的平台,从而彻底改变了容器化。网络是容器化的重要组成部分,Docker提供了各种网络驱动程序来支持容器之间的通信以...【详细内容】
2024-03-22    51CTO  Tags:Docker   点击:(11)  评论:(0)  加入收藏
Containerd容器管理
Nginx 指定容器名称 使用 ctr container create 命令创建容器后,容器并没有处于运行状态,其只是一个静态的容器。容器基本操作容器基本操作主要是 ctr image 命令,查看命令帮...【详细内容】
2024-03-20  云原生运维圈  微信公众号  Tags:容器   点击:(13)  评论:(0)  加入收藏
如何基于Docker镜像逆向生成Dockerfile
引言你是否曾经遇到过一个想要使用的 Docker 镜像,但却无法修改以适应你的特定需求?或者你可能发现了一个喜欢的 Docker 镜像,但想要了解它是如何构建的?在这两种情况下,将 Docke...【详细内容】
2024-03-07  云原生运维圈  微信公众号  Tags:Docker   点击:(23)  评论:(0)  加入收藏
Kubernetes是什么?主要特点是什么?
Kubernetes是什么?Kubernetes,也称为K8s,是一个开源的容器编排系统,由Google首次开发和维护。它允许容器化的应用程序在集群中自动部署、扩展和管理。Kubernetes提供了一种容器...【详细内容】
2024-02-01    简易百科  Tags:Kubernetes   点击:(162)  评论:(0)  加入收藏
我们一起聊聊容器资源自愈
在企业实际在使用容器这类资源的时候,除了技术本身,要考虑的其他问题也会很多。企业管理的容器有千千万万,出于效率考虑,对于有特殊需求的容器如何进行批量创建和管理呢,这就需要...【详细内容】
2024-01-30  匠心独运维妙维效  微信公众号  Tags:容器   点击:(47)  评论:(0)  加入收藏
Docker与Docker Compose入门:释放你应用部署的威力
今天给大家介绍一项强大而有趣的技能,那就是使用 Docker 和 Docker Compose 来释放你的应用部署的威力!无论你是一名开发人员还是系统管理员,掌握这个技能都将为你的工作带来巨...【详细内容】
2024-01-17  waynblog  微信公众号  Tags:Docker   点击:(66)  评论:(0)  加入收藏
Docker镜像与容器的交互及在容器内部执行代码的原理与实践
Docker作为一种流行的容器技术,已经成为现代应用程序开发和部署的重要工具。在Docker中,镜像是构建和运行容器的基础,而容器则是基于镜像创建的可执行实例。Docker镜像与容器的...【详细内容】
2024-01-10  编程技术汇  今日头条  Tags:Docker   点击:(79)  评论:(0)  加入收藏
如何在 Ubuntu 上安装 Docker
使用 Docker 意味着开启一个新的计算领域,但如果你刚刚开始使用 Docker,安装可能看起来是一项艰巨的任务。在 Ubuntu 上安装 Docker 有两种推荐的方法: 从 Ubuntu 的仓库安装 D...【详细内容】
2024-01-04    Linux中国  Tags:Docker   点击:(124)  评论:(0)  加入收藏
从Kubernetes的探针到DevOps
今天在群里又看有人问如何设置 Kubernetes 的探针,感觉要补充的话太多了,结合我们在一些 DevOps 项目中痛苦的体验,今天一劳永逸的全部说完,此外,也为大家展现一下为什么 DevOps...【详细内容】
2023-12-27  云云众生s  微信公众号  Tags:Kubernetes   点击:(117)  评论:(0)  加入收藏
站内最新
站内热门
站内头条