专栏首页大数据进阶dubbo源码分析之--java.lang.IllegalStateException: urls to invokers error .invokerUrls.size :1, invoker.si

dubbo源码分析之--java.lang.IllegalStateException: urls to invokers error .invokerUrls.size :1, invoker.si

最近在生产环境和预发环境经常出现java.lang.IllegalStateException: urls to invokers error .invokerUrls.size :1, invoker.size :0,这一类的error错误,具体错误信息如下

java.lang.IllegalStateException: urls to invokers error .invokerUrls.size :1, invoker.size :0. urls :[dubbo://192.168.137.13:20881/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=dubbodemoprovider&dubbo=2.5.3.6&interface=com.alibaba.dubbo.demo.DemoService&methods=complexInvoke,complex,Timeout,complexObject,complexUser,sayHello2,$$sayHelloTest,complexArray2,hessianTest,sayHello,HelloWorld,hessianMapTest,complexArray,hessianSerialable,ThrowException,mockDate&pid=3094&revision=2.5.3.8-SNAPSHOT&side=provider&timestamp=1493780202580] at com.alibaba.dubbo.registry.integration.RegistryDirectory.refreshInvoker(RegistryDirectory.java:229) [dubbo-2.5.3.9.jar:2.5.3.9] at com.alibaba.dubbo.registry.integration.RegistryDirectory.notify(RegistryDirectory.java:195) [dubbo-2.5.3.9.jar:2.5.3.9] at com.alibaba.dubbo.registry.support.AbstractRegistry.notify(AbstractRegistry.java:449) [dubbo-2.5.3.9.jar:2.5.3.9] at com.alibaba.dubbo.registry.support.FailbackRegistry.doNotify(FailbackRegistry.java:273) [dubbo-2.5.3.9.jar:2.5.3.9] at com.alibaba.dubbo.registry.support.FailbackRegistry.notify(FailbackRegistry.java:259) [dubbo-2.5.3.9.jar:2.5.3.9]

at com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.access$400(ZookeeperRegistry.java:45) [dubbo-2.5.3.9.jar:2.5.3.9] at com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry$3.childChanged(ZookeeperRegistry.java:163) [dubbo-2.5.3.9.jar:2.5.3.9] at com.alibaba.dubbo.remoting.zookeeper.zkclient.ZkclientZookeeperClient$2.handleChildChange(ZkclientZookeeperClient.java:82) [dubbo-2.5.3.9.jar:2.5.3.9] at org.I0Itec.zkclient.ZkClient$7.run(ZkClient.java:568) [zkclient-0.1.jar:na] at org.I0Itec.zkclient.ZkEventThread.run(ZkEventThread.java:71) [zkclient-0.1.jar:na]

首先出现该类型错误,并不影响正常调用,dubbo中有保护机制,当所有的的provider都被disable后,会保留最后一个被disable的provider,提供正常的服务。具体的实现代码如下RegistryDirectory类中的如下方法

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; // 禁止访问
        this.methodInvokerMap = null; // 置空列表
        destroyAllInvokers(); // 关闭所有Invoker
    } else {
        this.forbidden = false; // 允许访问
        Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
        if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
            invokerUrls.addAll(this.cachedInvokerUrls);
        } else {
            this.cachedInvokerUrls = new HashSet<URL>();
            this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
        }
        if (invokerUrls.size() ==0 ){
           return;
        }
        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表
        Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
        // state change
        //如果计算错误,则不进行处理.
        //这里是抛出具体错误的地方,当newUrlInvokerMap都为空时,这里会打印出ERROR日志
        if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
            logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
            return ;
        }
        this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
        this.urlInvokerMap = newUrlInvokerMap;
        try{
            destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
        }catch (Exception e) {
            logger.warn("destroyUnusedInvokers error. ", e);
        }
    }
}

具体是在进行灰度发布时,会先进行disable,会通过zookeeper通知到客户端,进行连接销毁,因为disable操作是单个进行,所以当操作到最后一个时,如果发现没有可用提供者,则会打印error日志,保留最后一个如下是AbstractRegistry类中的通知

protected void notify(URL url, NotifyListener listener, List<URL> urls) {
    if (url == null) {
        throw new IllegalArgumentException("notify url == null");
    }
    if (listener == null) {
        throw new IllegalArgumentException("notify listener == null");
    }
    if ((urls == null || urls.size() == 0) 
            && ! Constants.ANY_VALUE.equals(url.getServiceInterface())) {
        logger.warn("Ignore empty notify urls for subscribe url " + url);
        return;
    }
    if (logger.isDebugEnabled()) {
        logger.debug("Notify urls for subscribe url " + url + ", urls: " + urls);
    }
    Map<String, List<URL>> result = new HashMap<String, List<URL>>();
    for (URL u : urls) {
        if (UrlUtils.isMatch(url, u)) {
           String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
           List<URL> categoryList = result.get(category);
           if (categoryList == null) {
              categoryList = new ArrayList<URL>();
              result.put(category, categoryList);
           }
           categoryList.add(u);
        }
    }
    if (result.size() == 0) {
        return;
    }
    Map<String, List<URL>> categoryNotified = notified.get(url);
    if (categoryNotified == null) {
        notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
        categoryNotified = notified.get(url);
    }
    for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
        String category = entry.getKey();
        List<URL> categoryList = entry.getValue();
        categoryNotified.put(category, categoryList);
        saveProperties(url);
        listener.notify(categoryList);
    }
}

RegistryDirectory类中的通知

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());
        }
    }
    // configurators 
    if (configuratorUrls != null && configuratorUrls.size() >0 ){
        this.configurators = toConfigurators(configuratorUrls);
    }
    // routers
    if (routerUrls != null && routerUrls.size() >0 ){
        List<Router> routers = toRouters(routerUrls);
        if(routers != null){ // null - do nothing
            setRouters(routers);
        }
    }
    List<Configurator> localConfigurators = this.configurators; // local reference
    // 合并override参数
    this.overrideDirectoryUrl = directoryUrl;
    if (localConfigurators != null && localConfigurators.size() > 0) {
        for (Configurator configurator : localConfigurators) {
            this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
        }
    }
    // providers
    refreshInvoker(invokerUrls);
}

下面的连接销毁的方法是DubboInvoker中

//destroyUnusedInvokers这个方法中真正去销毁时会去调用该方法
public void destroy() {
        //防止client被关闭多次.在connect per jvm的情况下,client.close方法会调用计数器-1,当计数器小于等于0的情况下,才真正关闭
        if (super.isDestroyed()){
        return ;
    } else {
        //dubbo check ,避免多次关闭
        destroyLock.lock();
        try{
            if (super.isDestroyed()){
                return ;
            }
            super.destroy();
            if (invokers != null){
                invokers.remove(this);
            }
            for (ExchangeClient client : clients) {
                try {
                    client.close();
                } catch (Throwable t) {
                    logger.warn(t.getMessage(), t);
                }
            }
            
        }finally {
            destroyLock.unlock();
        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • dubbo源码分析之-Please check registry access list (whitelist/blacklist)

    看起来像是黑白名单,但是实际是服务方通过zk通知过来的地址变成了EMPTY://这样的形式,通常我们会提供如下的解决方式

    yiduwangkai
  • python技能(1)-map函数

    yiduwangkai
  • dubbo源代码-config

    yiduwangkai
  • Dubbo 中的 URL 统一模型

    在不谈及 dubbo 时,我们中的大多数人对 URL 这个概念并不会感到陌生。统一资源定位器 (RFC1738――Uniform Resource Locato...

    kirito-moe
  • Rancher 用户安装 Rainbond

    Rancher,Kubernetes 生态中成功的开源项目,其定位 “Run Kubernetes Everywhere”。Rancher 可以帮助开发者快速搭...

    Rainbond开源
  • RabbitMQ实战代码

    RabbitMQ 支持多种语言访问,以 Java 为例看下一般使用 RabbitMQ 的步骤。

    剑行者
  • EasyUI 中file-box的赋值问题

    $('#file_id').filebox('setvalue','文件路径'); 可以赋值,但是jq在控制台打印一个莫名其妙的错误,

    用户5640963
  • Supervisor 从入门到放弃

    简单点来讲,就是一个监控脚本运行的工具,不过他可以统一化管理,laravel的队列文档上也有相关使用方式方法,例如

    CrazyCodes
  • Java必备之从JDK到Dubbo的SPI深度剖析

    本示例路径META-INF/services/com.springboot.dubbo.spi.SayHelloService

    用户5397975
  • 使用flume完成数据的接收

    使用flume完成数据的接收 场景:source是通过tcp发送,chnnel处理过滤字段,sink存在集群中

    ZONGLYN

扫码关注云+社区

领取腾讯云代金券