文档章节

从Dubbo源码分析RpcException:Forbid consumer

向码而生
 向码而生
发布于 2017/09/12 00:58
字数 1764
阅读 267
收藏 0
点赞 0
评论 0

     跑的好好的服务突然报异常:com.alibaba.dubbo.RpcException: Forbid consumer ${ip} access service ... use dubbo version 2.5.3, Please check registry access list (whitelist/blacklist),是否有点莫名其妙?TMD,还是先追查问题的ROOT CAUSE,再骂街。。。

    来来来,按国际惯例,先把Stack Trace贴出来【注释掉公司敏感信息】:

    

    根据Stack Trace,该RpcException是由dubbo源码中类RegistryDirectory的doList方法抛出的,找到RegistryDirectory.java的第579行doList()方法,少废话,直接上代码:

public List<Invoker<T>> doList(Invocation invocation) {
        if (forbidden) {
            throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "Forbid consumer " +  NetUtils.getLocalHost() + " access service " + getInterface().getName() + " from registry " + getUrl().getAddress() + " use dubbo version " + Version.getVersion() + ", Please check registry access list (whitelist/blacklist).");
        }
        
        //此处省略无关紧要的N行

        return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
    }

What? 当forbidden值为true时,抛出该异常?变量forbidden是个什么鬼东西?

/**
 * RegistryDirectory
 * 
 * @author william.liangf
 * @author chao.liuc
 */
public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {

    private static final Logger logger = LoggerFactory.getLogger(RegistryDirectory.class);
    
    // 此处删除无关紧要的N行

    private volatile boolean forbidden = false;


    // 后面删除无关紧要的N行

}

从类RegistryDirectory中发现改值默认初始化为false? 拿尼?谁TMD把老子的forbidden值给改成了true?

Find Usage, 来找一下forbidden都在哪里用了:

次奥,让类RegistryDirectory的refreshInvoker(List<URL>)方法给改成了true?来来来,上代码,贴证据:

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; // 允许访问
            
        // 此处删除代码无数行 -- 不关心,就是这么任性!

    }

TMD,来查下是谁暗地里“指使”你改的“forbidden=true”,谁动了我的代码,一个都别跑。

还是RegistryDirectory这个类,有一个叫notify(List<URL>)的方法:这又是个什么鬼?看最后一句代码,确实是它动了,证据确凿!

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

这个notify(List<URL>)是个什么鬼东西,为何改我的代码?

public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
    // 删除代码无数行
}

TMD又是RegistryDirectory ?不对,这次不是你,是你爹NotifyListener。

package com.alibaba.dubbo.registry;
import java.util.List;
import com.alibaba.dubbo.common.URL;
/**
 * NotifyListener. (API, Prototype, ThreadSafe)
 * 
 * @see com.alibaba.dubbo.registry.RegistryService#subscribe(URL, NotifyListener)
 * @author william.liangf
 */
public interface NotifyListener {

    /**
     * 当收到服务变更通知时触发。
     * 
     * 通知需处理契约:<br>
     * 1. 总是以服务接口和数据类型为维度全量通知,即不会通知一个服务的同类型的部分数据,用户不需要对比上一次通知结果。<br>
     * 2. 订阅时的第一次通知,必须是一个服务的所有类型数据的全量通知。<br>
     * 3. 中途变更时,允许不同类型的数据分开通知,比如:providers, consumers, routers, overrides,允许只通知其中一种类型,但该类型的数据必须是全量的,不是增量的。<br>
     * 4. 如果一种类型的数据为空,需通知一个empty协议并带category参数的标识性URL数据。<br>
     * 5. 通知者(即注册中心实现)需保证通知的顺序,比如:单线程推送,队列串行化,带版本对比。<br>
     * 
     * @param urls 已注册信息列表,总不为空,含义同{@link com.alibaba.dubbo.registry.RegistryService#lookup(URL)}的返回值。
     */
    void notify(List<URL> urls);

}

这次都是你爹惹的祸,自己不做,指使你去notify。先看下你爹的“供词”

    “当收到服务变更通知时触发”,看来背后还有大BOSS ?

根据“证词”,得知部分真相如下:

    “Zookeeper收到服务变更通知时,都会触发对notify方法的调用”,TMD叼爆了,大BOSS让干啥,你就干啥,自己不亲自干,还让你儿子去干!先别动大BOSS,先来看看你儿子这傻叉都干了些啥?

先把证据摆出来:

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; // 允许访问

        // 后面统统删除。。。
}

看下你改forbidden=true都收了什么好处?

好处1: invokerUrls != null

好处2: invokerUrls.size() == 1

好处3: invokerUrls.get(0) != null

好处4: Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())

什么??我眼睛没瞎?你竟然同时拿了四个好处?我滴乖乖。。

分析下你拿的四个好处,一时竟然不知如何断案!!!先不关你invokerUrls到底是谁,看下好处4:

Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol()) 协议是空(empty)? 一个zookeeper推送服务注册信息变更,就把协议改成了empty ?

继续Find Usage:

有了新的线索:类ZookeeperRegistry的toUrlsWithEmpty(URL, String, List<String>)方法把protocol设置成了empty!证据确凿,找到物证如下:

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

什么?urls==null 或者 urls.isEmpty(),你就把protocol设置成empty?这是为啥?继续追踪urls...

    private List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
    	List<URL> urls = new ArrayList<URL>();
        if (providers != null && providers.size() > 0) {
            for (String provider : providers) {
                provider = URL.decode(provider);
                if (provider.contains("://")) {
                    URL url = URL.valueOf(provider);
                    if (UrlUtils.isMatch(consumer, url)) {
                        urls.add(url);
                    }
                }
            }
        }
        return urls;
    }

来来来,分析下案情:toUrlsWithoutEmpty的结果是空或者size为0,则强制返回一个protocol为empty的url,看来源头就在这里了。传入的List<String> providers实际上就是最新的服务提供者信息,当某个服务没有任何provider时,providers就变为一个size为0的List了,导致返回一个协议头为empty的url,进而导致forbidden为true,屏蔽了consumer调用。

来看下细节:UrlUtils.isMatch(consumer, url)?匹配(true)怎样?不匹配(false)又怎样?

    public static boolean isMatch(URL consumerUrl, URL providerUrl) {
        String consumerInterface = consumerUrl.getServiceInterface();
        String providerInterface = providerUrl.getServiceInterface();
        if( ! (Constants.ANY_VALUE.equals(consumerInterface) || StringUtils.isEquals(consumerInterface, providerInterface)) ) return false;
        
        if (! isMatchCategory(providerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY), 
                consumerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY))) {
            return false;
        }
        if (! providerUrl.getParameter(Constants.ENABLED_KEY, true) 
                && ! Constants.ANY_VALUE.equals(consumerUrl.getParameter(Constants.ENABLED_KEY))) {
            return false;
        }
       
        String consumerGroup = consumerUrl.getParameter(Constants.GROUP_KEY);
        String consumerVersion = consumerUrl.getParameter(Constants.VERSION_KEY);
        String consumerClassifier = consumerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
        
        String providerGroup = providerUrl.getParameter(Constants.GROUP_KEY);
        String providerVersion = providerUrl.getParameter(Constants.VERSION_KEY);
        String providerClassifier = providerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
        return (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
               && (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
               && (consumerClassifier == null || Constants.ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));
    }

什么情况是匹配?return true.

什么情况不匹配?return false.

return (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
               && (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
               && (consumerClassifier == null || Constants.ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));

什么?消费者和生产者的group(分组), version(版本),classifier(路由?)必须一致?否则return false, 并将urls就是null, 同时

URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category); 

然后就发生了最初的惨剧:this.forbidden=true.

if (forbidden) {
     throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "Forbid consumer " +  NetUtils.getLocalHost() + " access service " + getInterface().getName() + " from registry " + getUrl().getAddress() + " use dubbo version " + Version.getVersion() + ", Please check registry access list (whitelist/blacklist).");
}

 

分析完案情,来看看现场的情况如何?

【以上敏感信息打了马赛克】consumerVersion="1.0.0", 然而providerVersion="1.0.1", Amazing....与案情分析完全一致。

So, What the fuck ?  到底是谁升级了服务器dubbo服务的版本?不说了,说多了都是马赛克

 

© 著作权归作者所有

共有 人打赏支持
向码而生
粉丝 5
博文 9
码字总数 9257
作品 0
西城
个人站长
解决dubbo问题:forbid consumer

解决dubbo问题:forbid consumer By admin | 2015/06/09 0 Comment 线下环境经常出现类似这种异常: com.alibaba.dubbo.rpc.RpcException: Forbid consumer 10.0.53.69 access service com.......

磨砂轮
2015/10/28
0
7
Dubbo的com.alibaba.dubbo.monitor.MonitorService提供者的问题

今天对集群做测试时发现消费者上一直抛这个错误: [somnus][INFO] [2016-07-17 17:07:54] | [DUBBO] Send statistics to monitor zookeeper://127.0.0.1:2181/com.alibaba.dubbo.monitor.Mon......

500_Server_Error
2016/07/17
6.4K
4
dubbo源码分析系列(3)服务的引用

1 系列目录 - dubbo源码分析系列(1)扩展机制的实现- dubbo源码分析系列(2)服务的发布- dubbo源码分析系列(3)服务的引用- dubbo源码分析系列(4)dubbo通信设计 2 服务引用案例介绍 先看...

乒乓狂魔
2015/10/12
3.2K
5
dubbo源码分析系列(2)服务的发布

1 系列目录 - dubbo源码分析系列(1)扩展机制的实现- dubbo源码分析系列(2)服务的发布- dubbo源码分析系列(3)服务的引用- dubbo源码分析系列(4)dubbo通信设计 2 dubbo与spring接入 du...

乒乓狂魔
2015/09/28
6.3K
4
dubbo问题,急需解决!!!!

dubbo服务提供者启动了,消费之报错 com.alibaba.dubbo.rpc.RpcException: Failed to register subscribe://192.168.28.101/com.dubbo.consumer.DemoService?application=penngo-consumer&d......

luckymen
2015/12/28
1K
0
dubbo客服端连接不上

server的配置

鸟菜啊
2016/03/11
3.4K
3
Dubbo 2.0.9 发布,阿里巴巴开源服务框架

阿里巴巴开源服务框架Dubbo2.0.9版本发布了,该版本增加了简易监控中心界面,以及修复了一些BUG。 Dubbo首页:http://code.alibabatech.com/wiki/display/dubbo/Home 下载地址:http://code...

红薯
2011/12/14
15.3K
6
dubbo如何调用hessian或webservice协议暴露的接口??求大神帮助

服务地址:hessian://192.168.1.156:8086/com.houg.webservice.service.ComputeService?anyhost=true&application=calculator-app&default.timeout=5000&dubbo=2.5.3&dynamic=true&interface......

侯刚
2014/11/06
7.6K
0
com.alibaba.dubbo.rpc.RpcException: 怎么就解决

com.alibaba.dubbo.rpc.RpcException: Forbid consumer 192.168.127.1 access service com.diit.facade.operation.service.UserFacade from registry 127.0.0.1:2181 use dubbo version 2.8.......

sami110
2016/11/14
5.8K
3
阿里巴巴开源服务框架 Dubbo 2.0.10 发布

Dubbo是阿里巴巴SOA服务化治理方案的核心框架,每天为1,000+个服务提供1,000,000,000+次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。 2011-12-28发布最新可靠版本2.0.10,该版本修...

SDK4
2011/12/28
16.5K
11

没有更多内容

加载失败,请刷新页面

加载更多

下一页

如何把你的Linux系统变的更加安全

做为一个小白,以为自己懂了点Linux知识,会搭建Linux各种服务就觉得自己牛的不要要的。在我们团队里面,我将使用了一台破电脑搭建Linux服务器,上面跑着Ftp服务存放着资源,ssh服务可以远程...

问题终结者
1分钟前
0
0
lombok的使用和原理

一、项目背景 在写Java程序的时候经常会遇到如下情形: 新建了一个Class类,然后在其中设置了几个字段,最后还需要花费很多时间来建立getter和setter方法 lombok项目的产生就是为了省去我们手...

颖辉小居
1分钟前
0
0
rsync至服务同步-系统日志-screen

rsync: 服务同步;配置文件:/etc/rsyncd.conf 默认端口:873 服务启动:rsync --daemon rsync -av /root/1.txt 192.168.1.2::test/2.txt (test为模块名称) /etc/rsync.conf配置样例: #指定...

ZHENG-JY
3分钟前
0
0
读取文件中内容转换成字符串

package com.lieni.ruyu.api.xmlTool; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Unsuppo......

newdeng
4分钟前
0
0
《PHP和MySQL Web 开发》 第8章 设计Web数据库

LCL WARNING 这是我学习《PHP和MySQL Web 开发》的读书笔记,一些重要的知识点我会记录下来,当然只会写我觉得重要的。 如果有幸有人看到这个学习笔记了,你要结合着书看,不要光看这个笔记。...

十万猛虎下画山
11分钟前
0
0
Spring+jpaNo transactional EntityManager available

TransactionRequiredException: No transactional EntityManager availableEntityManager执行以下方法(refresh, persist, flush, joinTransaction, remove, merge) 都需要需要事务i......

wpfc
11分钟前
0
0
八幅漫画理解使用JSON Web Token设计单点登录系统

八幅漫画理解使用JSON Web Token设计单点登录系统 Sep 07, 2015 in Engineering 上次在《JSON Web Token - 在Web应用间安全地传递信息》中我提到了JSON Web Token可以用来设计单点登录系统。...

祖冲之
13分钟前
0
0
Spring框架中的设计模式(三)

Spring框架中的设计模式(三) 原创: 瑞查德-Jack 在之前的两篇文章中,我们看到了一些在Spring框架中实现的设计模式。这一次我们会发现这个流行框架使用的3种新模式。 本文将从描述两个创意...

瑞查德-Jack
16分钟前
1
0
[MicroPython]TPYBoard智能小车“飞奔的TPYBoard装甲一号”

智能小车作为现代的新发明,是以后的发展方向,他可以按照预先设定的模式在一个环境里自动的运作,不需要人为的管理,可应用于科学勘探等等的用途。智能小车能够实时显示时间、速度、里程,具...

bodasisiter
18分钟前
0
0
桌面虚拟化VDI(Virtual Desktop Infrastructure)

为了保证员工(客户)不把公司的资料复制、传输给别人。可以把员工平时办公放在服务器上做。所以使用桌面虚拟化。就是把一个服务器虚拟出很多桌面系统(如:windows)。 桌面虚拟化最大的优势...

王坤charlie
26分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部