dubbo版本
- dubbo版本2.6.7
No provider available from registry
-
如果没有服务提供者,消费者会抛出异常
No provider available from registry dubbo-zookeeper:2181 for service group0/cn.jannal.dubbo.facade.DemoService:1.0.0 on consumer 172.16.117.33 use dubbo version 2.6.7, please check status of providers(disabled, not registered or in blacklist).
-
上面的异常是在
RegistryDirectory#doList
方法中抛出。可以看到当forbidden=true
才抛出异常public List<Invoker<T>> doList(Invocation invocation) { // 服务提供者关闭或禁用了服务,此时抛出 No provider 异常 if (forbidden) { // 1. No service provider 2. Service providers are disabled throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist)."); } ...省略... }
-
forbidden设置为true
是在RegistryDirectory#refreshInvoker
设置的。refreshInvoker
是在RegistryDirectory#notify
中被调用。 当某个服务的provider有变化时RegistryDirectory#notify
就会被调用(例如zookeeper上某个服务的provider目录里的内容发生变化,则zk监听器会被触发)// URL的protocol是如何变为empty的? private void refreshInvoker(List<URL> invokerUrls) { if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { this.forbidden = true; // Forbid to access this.methodInvokerMap = null; // Set the method invoker map to null destroyAllInvokers(); // Close all invokers } ...省略... } @Override public synchronized void notify(List<URL> urls) { List<URL> invokerUrls = new ArrayList<URL>(); List<URL> routerUrls = new ArrayList<URL>(); List<URL> configuratorUrls = new ArrayList<URL>(); for (URL url : urls) { String protocol = url.getProtocol(); String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY); if (Constants.ROUTERS_CATEGORY.equals(category) || Constants.ROUTE_PROTOCOL.equals(protocol)) { routerUrls.add(url); } else if (Constants.CONFIGURATORS_CATEGORY.equals(category) || Constants.OVERRIDE_PROTOCOL.equals(protocol)) { configuratorUrls.add(url); } else if (Constants.PROVIDERS_CATEGORY.equals(category)) { invokerUrls.add(url); } else { logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()); } } ...省略... // providers refreshInvoker(invokerUrls); }
-
查看notify URL发现URL中的协议是empty。那URL的protocol是如何变为empty的?
-
查看
ZookeeperRegistry#doSubscribe
,zookeeper初次订阅或者订阅的信息有变更时,都会触发ChildListener的childChanged方法,此方法调用前会调用toUrlsWithEmpty方法进行empty协议的设置。即如果consumer的URL与所有provider都不匹配,则将consumer的protocol设置为empty协议,从而最终导致forbidden=true
protected void doSubscribe(final URL url, final NotifyListener listener){ ...省略... ChildListener zkListener = listeners.get(listener); if (zkListener == null) { listeners.putIfAbsent(listener, new ChildListener() { @Override public void childChanged(String parentPath, List<String> currentChilds) { ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)); } }); zkListener = listeners.get(listener); } ...省略... } private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) { List<URL> urls = toUrlsWithoutEmpty(consumer, providers); //如果consumer的URL与所有provider都不匹配,则将consumer的protocol设置为empty协议 if (urls == null || urls.isEmpty()) { int i = path.lastIndexOf('/'); String category = i < 0 ? path : path.substring(i + 1); URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category); urls.add(empty); } return urls; } private List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) { List<URL> urls = new ArrayList<URL>(); if (providers != null && !providers.isEmpty()) { //遍历所有provider for (String provider : providers) { provider = URL.decode(provider); if (provider.contains("://")) { URL url = URL.valueOf(provider); //如果与consumer的URL匹配,则添加url列表中 if (UrlUtils.isMatch(consumer, url)) { urls.add(url); } } } } return urls; }