• dubbo-无法找到提供者问题(一)


    今天启动dubbo,提供者没问题,消费者报错,提示 没有可用的提供者

    然后具体跟了以下dubbo代码:

    根据报错的位置是在
    org.apache.dubbo.config.ReferenceConfig#checkInvokerAvailable

        private void checkInvokerAvailable() throws IllegalStateException {
            if (shouldCheck() && !invoker.isAvailable()) {//shouldCheck() 默认是开启检查的,invoker.isAvailable()判断是否可用
                invoker.destroy();
                throw new IllegalStateException("Failed to check the status of the service "
                        + interfaceName
                        + ". No provider available for the service "
                        + (group == null ? "" : group + "/")
                        + interfaceName +
                        (version == null ? "" : ":" + version)
                        + " from the url "
                        + invoker.getUrl()
                        + " to the consumer "
                        + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    问题应该是就出在了 invoker.isAvailable()中,其中invoker是由

    invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));

    产生的,所有应该是DubboInvoker,我们看下DubboInvoker

        @Override
        public boolean isAvailable() {
            if (!super.isAvailable()) {
                return false;
            }
            for (ExchangeClient client : clients) {
                if (client.isConnected() && !client.hasAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY)) {
                    //cannot write == not Available ?
                    return true;
                }
            }
            return false;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    根据断点提示 client.isConnected() 等于 false,说明此处client是不可用的,继续进去client.isConnected()的方法
    org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeClient#isConnected

        @Override
        public boolean isConnected() {
            return channel.isConnected();
        }
    
    • 1
    • 2
    • 3
    • 4

    org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeChannel#isConnected

        @Override
        public boolean isConnected() {
            return channel.isConnected();
        }
    
    • 1
    • 2
    • 3
    • 4

    此处我们看下channel的对接端信息
    在这里插入图片描述
    这就奇怪了,原本我是单机起的两个服务进行调用,为啥ip不一致呢,目前通过 ifconfig 查询 我本机ip应该是 192.168.124.2,即消费端ip是正确的,提供端ip错误了。

    然后我们定位下 服务端的 ip 获取方式,因为RegistryProtocol是开始zk注册的入口,我们直接看下 org.apache.dubbo.registry.integration.RegistryProtocol#export

        @Override
        public  Exporter export(final Invoker originInvoker) throws RpcException {
            URL registryUrl = getRegistryUrl(originInvoker);
            //此方法用于组织提供端的服务信息,所以我们断点看下providerUrl信息
            URL providerUrl = getProviderUrl(originInvoker);
            。。。
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    由图可以看到 此处已经获取到了一个错误的ip

    我们继续看下如何获取的这个错误ip

        private URL getProviderUrl(final Invoker originInvoker) {
        	//String EXPORT_KEY = "export"
            Object providerURL = originInvoker.getUrl().getAttribute(EXPORT_KEY);
            if (!(providerURL instanceof URL)) {
                throw new IllegalArgumentException("The registry export url is null! registry: " + originInvoker.getUrl().getAddress());
            }
            return (URL)providerURL;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    由此处我们得知 ip是url中存储的,对于存储 我们知道在 ServiceConfig的doExportUrlsFor1Protocol方法中会存储map信息到url中

        private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List registryURLs) {
        	、、、
        	//获取host
    		String host = findConfigedHosts(protocolConfig, registryURLs, map);
            Integer port = findConfigedPorts(protocolConfig, name, map);
            URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
    		、、、
    		Invoker invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, 
    		//EXPORT_KEY = "export",将url保存进url中
    registryURL.putAttribute(EXPORT_KEY, url));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我们马上就接近真像了,继续看下findConfigedHosts方法

        private String findConfigedHosts(ProtocolConfig protocolConfig,
                                         List registryURLs,
                                         Map map) {
                                     
           、、、
           logger.info("No valid ip found from environment, try to find valid host from DNS.");
           hostToBind = InetAddress.getLocalHost().getHostAddress();
           、、、
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后我们执行下 InetAddress.getLocalHost().getHostAddress() 发现 确实是 错误的ip:192.168.1.3

    为什么呢?
    此处我们找到了问题的根源,当前我们的mac环境的hostname是篡改为了虚拟的,

    wangwenyong@wangwenyong ~ % hostname
    bogon

    具体查看文章中解释及修复方式: 点击此链接

    既然我们看到了这里,索性 看下 消费端的获取方式是什么:

    我们可以从org.apache.dubbo.registry.integration.RegistryProtocol#refer入手,既然订阅 肯定要有自己的ip

    	public  Invoker refer(Class type, URL url) throws RpcException {
    		、、、
    		//REFER_KEY = "refer"
    		Map qs = (Map)url.getAttribute(REFER_KEY);
    		、、、
    		return doRefer(cluster, registry, type, url, qs);
    	}
    
    
        protected  Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url, Map parameters) {
        	//REGISTER_IP_KEY = "register.ip"
            URL consumerUrl = new URL(parameters.get(PROTOCOL_KEY) == null ? DUBBO : parameters.get(PROTOCOL_KEY), parameters.get(REGISTER_IP_KEY), 0, getPath(parameters, type), parameters);
            url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl);
            ClusterInvoker migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl);
            return interceptInvoker(migrationInvoker, url, consumerUrl, url);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    我们看下 url中的refer中的register.ip的存储时机的代码,毫无疑问 在org.apache.dubbo.config.ReferenceConfig中
    我们直接搜索 REGISTER_IP_KEY便可定位到 在org.apache.dubbo.config.ReferenceConfig#init中

    	public synchronized void init() {
    		、、、
            String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY);
            if (StringUtils.isEmpty(hostToRegistry)) {
            	//此处为获取ip
                hostToRegistry = NetUtils.getLocalHost();
            } else if (isInvalidLocalHost(hostToRegistry)) {
                throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
            }
            map.put(REGISTER_IP_KEY, hostToRegistry);
            、、、
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    所以我们得出结论 在消费端 使用的 NetUtils.getLocalHost() 来获取,提供端用的 InetAddress.getLocalHost().getHostAddress()导致的ip不一致,又因为 计算机名错误 获取ip错误

    问题的发现多谢了 http://blog.chinaunix.net/uid-540802-id-138782.html 文章的指点

  • 相关阅读:
    【LeetCode-中等题】106. 从中序与后序遍历序列构造二叉树
    Ubuntu16.04安装网卡驱动
    2039: [蓝桥杯2022初赛] 李白打酒加强版 (动态规划)
    Law of continuity
    OpenGL之图形流水线中的光照计算、明暗处理
    简述WPF中MVVM的设计思想
    常见分词算法综述
    基于SpringBoot的中小企业设备管理系统
    首次认定20万!2022年武汉市汉阳区高新技术企业认定材料、条件和奖励补助
    嵌入式软件开发新趋势:DevOps
  • 原文地址:https://blog.csdn.net/m0_54850825/article/details/126327874