dubbo消费者启动报错Failed to check the status of the service

1,716 阅读2分钟

dubbo消费者启动报: Failed to check the status of the service

dubbo版本 : 2.7.7

consumer 使用 @DubboReference 注解, 启动报错:

Injection of @DubboReference dependencies is failed; nested exception is java.lang.IllegalStateException: Failed to check the status of the service *** No provider available for the service *** from the url zookeeper

  • 去zookeeper 节点查看确实存在provider服务 ,也确定配置、jar包、网络都没问题,最后定位到原来是因为provider指定了版本号,而consumer没有指定版本号,所以才抛出这个错误。

spring容器启动时,执行 doCreateBean创建对象时,碰到属性有@DubboReference注解,会执行ReferenceConfig的get()方法,然后执行init()方法

public synchronized T get() {
    if (destroyed) {
        throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
    }
    if (ref == null) {
        init();
    }
    return ref;
}

init()调用 checkAndUpdateSubConfigs()方法

public void checkAndUpdateSubConfigs() {
    
    //实例化对象
        try {
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                    .getContextClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        checkInterfaceAndMethods(interfaceClass, getMethods());
    
    // 设置属性
    //init serivceMetadata
    serviceMetadata.setVersion(version);
    serviceMetadata.setGroup(group);
    serviceMetadata.setDefaultGroup(group);
    serviceMetadata.setServiceType(getActualInterface());
    serviceMetadata.setServiceInterfaceName(interfaceName);
    // TODO, uncomment this line once service key is unified
     //根据接口名,group 和 version 去构建服务key
    serviceMetadata.setServiceKey(URL.buildKey(interfaceName, group, version));

    ServiceRepository repository = ApplicationModel.getServiceRepository();
    ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
    // 注册consumer 
    repository.registerConsumer(
            serviceMetadata.getServiceKey(),
            serviceDescriptor,
            this,
            null,
            serviceMetadata);

    resolveFile();
    ConfigValidationUtils.validateReferenceConfig(this);
    postProcessConfig();
}

然后回到init()方法的 ref = createProxy(map) 方法;

private T createProxy(Map<String, String> map) {


if (urls.size() == 1) {
    invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} 

然后执行 RegistryProtocol 的doRefer

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    // 根据url订阅服务
    directory.subscribe(toSubscribeUrl(subscribeUrl));

    Invoker<T> invoker = cluster.join(directory);
    List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
    if (CollectionUtils.isEmpty(listeners)) {
        return invoker;
    }

FialbackRegistry

@Override
public void subscribe(URL url, NotifyListener listener) {
    super.subscribe(url, listener);
    removeFailedSubscribed(url, listener);
    try {
        // Sending a subscription request to the server side
        doSubscribe(url, listener);
    } catch (Exception e) {
        Throwable t = e;

ZookeeperRegistry

@Override
public void doSubscribe(final URL url, final NotifyListener listener) {
if (ANY_VALUE.equals(url.getServiceInterface())) {
   // ···省略代码···
} else {
    List<URL> urls = new ArrayList<>();
    for (String path : toCategoriesPath(url)) {
        ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
        ChildListener zkListener = listeners.computeIfAbsent(listener, k -> (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, k, toUrlsWithEmpty(url, parentPath, currentChilds)));
        zkClient.create(path, false);
        // 获取zk节点数据
        List<String> children = zkClient.addChildListener(path, zkListener);
        if (children != null) {
            urls.addAll(toUrlsWithEmpty(url, path, children));
        }
    }
    notify(url, listener, urls);
}

private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
    List<URL> urls = toUrlsWithoutEmpty(consumer, providers);
    if (urls == null || urls.isEmpty()) {
        int i = path.lastIndexOf(PATH_SEPARATOR);
        String category = i < 0 ? path : path.substring(i + 1);
        URL empty = URLBuilder.from(consumer)
                .setProtocol(EMPTY_PROTOCOL)
                .addParameter(CATEGORY_KEY, category)
                .build();
        urls.add(empty);
    }
    return urls;
}


private List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
    List<URL> urls = new ArrayList<>();
    if (CollectionUtils.isNotEmpty(providers)) {
        for (String provider : providers) {
            if (provider.contains(PROTOCOL_SEPARATOR_ENCODED)) {
                URL url = URLStrParser.parseEncodedStr(provider);
                // 比较zk节点数据, 如果相同则添加进去
                if (UrlUtils.isMatch(consumer, url)) {
                    urls.add(url);
                }
            }
        }
    }
    return urls;
}

UrlUtils

public static boolean isMatch(URL consumerUrl, URL providerUrl) {
    String consumerInterface = consumerUrl.getServiceInterface();
    String providerInterface = providerUrl.getServiceInterface();
    //FIXME accept providerUrl with '*' as interface name, after carefully thought about all possible scenarios I think it's ok to add this condition.
    if (!(ANY_VALUE.equals(consumerInterface)
            || ANY_VALUE.equals(providerInterface)
            || StringUtils.isEquals(consumerInterface, providerInterface))) {
        return false;
    }

    if (!isMatchCategory(providerUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY),
            consumerUrl.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY))) {
        return false;
    }
    if (!providerUrl.getParameter(ENABLED_KEY, true)
            && !ANY_VALUE.equals(consumerUrl.getParameter(ENABLED_KEY))) {
        return false;
    }

    String consumerGroup = consumerUrl.getParameter(GROUP_KEY);
    String consumerVersion = consumerUrl.getParameter(VERSION_KEY);
    String consumerClassifier = consumerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);

    String providerGroup = providerUrl.getParameter(GROUP_KEY);
    String providerVersion = providerUrl.getParameter(VERSION_KEY);
    String providerClassifier = providerUrl.getParameter(CLASSIFIER_KEY, ANY_VALUE);
    // 问题出现再这里, provider指定了版本,而consumer没有指定,所以这里返回false 
    return (ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
            && (ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
            && (consumerClassifier == null || ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));
}

通过RegistryDiretory类的 refreshInvoker()方法,给urlInvokerMap赋值,

最后看 ReferenceConfig的

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

如果check = true ,但是没有找到provinder,则抛出异常;

所有的bean实例化后,开始刷新容器 , AbstractApplicationContext - spring容器启动时,会发布 ContextRefreshedEvent 事件。

protected void finishRefresh() {
     ··· 省略代码 ···
   // Publish the final event.
   publishEvent(new ContextRefreshedEvent(this));
   ··· 省略代码 ···
}

OneTimeExecutionApplicationContextEventListener - 定义监听器,监听spring的 ContextRefreshedEvent 事件。

abstract class OneTimeExecutionApplicationContextEventListener implements ApplicationListener, ApplicationContextAware {

    private ApplicationContext applicationContext;

    public final void onApplicationEvent(ApplicationEvent event) {
        if (isOriginalEventSource(event) && event instanceof ApplicationContextEvent) {
           
            onApplicationContextEvent((ApplicationContextEvent) event);
        }
    }

    ··· 省略代码 ···
}

DubboBootstrapApplicationListener - 判断如果是:ContextRefreshedEvent 事件则启动dubbo , dubboBootstrap.start()

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
        implements Ordered {

   ··· 省略代码 ···

    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
    
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }

     ··· 省略代码 ···
}

spring容器在刷新时,发布 ContextRefreshedEvent事件, 开始启动dubbo服务:

publishEvent(new ContextRefreshedEvent(this));  
    -->   onContextRefreshedEvent((ContextRefreshedEvent) event);
    -->   dubboBootstrap.start();       

DubboBootstrap

start()方法

public DubboBootstrap start() {
    if (started.compareAndSet(false, true)) {
        ready.set(false);
        initialize();
        if (logger.isInfoEnabled()) {
            logger.info(NAME + " is starting...");
        }
        // 1. export Dubbo Services
        exportServices();

        // Not only provider register
        if (!isOnlyRegisterProvider() || hasExportedServices()) {
            // 2. export MetadataService
            exportMetadataService();
            //3. Register the local ServiceInstance if required
            registerServiceInstance();
        }
     
      referServices();
      ··· 省略代码 ···
}