原文地址:

http://www.jameswxx.com/%e4%b8%ad%e9%97%b4%e4%bb%b6/%e8%a7%a3%e5%86%b3dubbo%e9%97%ae%e9%a2%98%ef%bc%9aforbid-consumer/

线下环境经常出现类似这种异常:

com.alibaba.dubbo.rpc.RpcException: Forbid consumer   access service   from registry   use dubbo version 2.5.3, Please check registry access list (whitelist/blacklist).
大致意思是当前调用者被禁止访问某个服务,请检查下注册中心访问列表,还有黑名单和白名单。
其实线下环境根本没有对服务做白名单和黑名单机制,因为线下环境给开发人员的账号是guest,没有权限做黑白名单。今天有好几个人问我这个问题,我仔细看了源码,找出了根源所在。
根据异常栈,抛出这个异常的代码在RegistryDirectory的第579行,如下:


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).”


);

}

如果forbidden变量为true,则抛出该异常。forbidden变量默认为false,那么什么时候变成true了呢?看

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

}

意思是如果

invokerUrls的size为1,并且url的协议头是

Constants.



EMPTY_PROTOCOL时,则设置forbidden为false,



Constants.



EMPTY_PROTOCOL的值是empty。







refreshInvoker方法什么时候被调用呢?当某个服务的provider有变化时就会被调用,例如zookeeper上某个服务的provider目录里的内容发生变化,则zk监听器会被触发,由于provider的数量会发生变化,例如有一个新的provider启动了,有一个provider下线了,所以必须刷新本地的对provider的连接,具体逻辑就在refreshInvoker方法里,这个方法的调用栈如下:

可以确定的是,zookeeper推送的URL的protocol部分不可能无缘无故变成了empty,肯定是由某个地方更改了,于是看一下

Constants.



EMPTY_PROTOCOL到底有哪些地方调用了,如下:

见图中红色圈圈部分,当zookeeper初次订阅或者订阅的信息有变更时,都会触发toUrlsChanged方法,看看这个方法内部都做了什么,完整代码如下:




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;

}


可见如果

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


Category:

中间件



标签:

dubbo