Dubbo常见错误分析

目录

dubbo版本

  1. dubbo版本2.6.7

No provider available from registry

  1. 如果没有服务提供者,消费者会抛出异常

    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).
    
  2. 上面的异常是在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).");
        }
    		...省略...
    }  
    
  3. 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);
    }
    
  4. 查看notify URL发现URL中的协议是empty。那URL的protocol是如何变为empty的?

    Dubbo常见错误分析

  5. 查看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;
    }
    
上一篇:钉钉群组实现监控Github的CVE信息


下一篇:飞机发动机测试台行业研究及十四五规划分析报告