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

问题应该是就出在了 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;
    }

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

    @Override
    public boolean isConnected() {
        return channel.isConnected();
    }

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

    @Override
    public boolean isConnected() {
        return channel.isConnected();
    }

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

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

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

在这里插入图片描述
由图可以看到 此处已经获取到了一个错误的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;
    }

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

    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> 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));
	}

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

    private String findConfigedHosts(ProtocolConfig protocolConfig,
                                     List<URL> registryURLs,
                                     Map<String, String> map) {
                                 
       、、、
       logger.info("No valid ip found from environment, try to find valid host from DNS.");
       hostToBind = InetAddress.getLocalHost().getHostAddress();
       、、、
    }

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

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

wangwenyong@wangwenyong ~ % hostname
bogon

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

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

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

	public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
		、、、
		//REFER_KEY = "refer"
		Map<String, String> qs = (Map<String, String>)url.getAttribute(REFER_KEY);
		、、、
		return doRefer(cluster, registry, type, url, qs);
	}


    protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url, Map<String, String> 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<T> migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl);
        return interceptInvoker(migrationInvoker, url, consumerUrl, url);
    }

在这里插入图片描述
我们看下 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);
        、、、
    }

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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值