今日在做maven项目访问url:http://localhost:9101/admin/type_template.html时,一直出不来效果,控制台报如下错误:
com.alibaba.dubbo.rpc.RpcException:Forbid consumer 192.168.80.6 access service com.xxx.xxx.xxx(此处是你的TypeTemplateService的包名).TypeTemplateService from registry 192.168.25.130:2181 use dubbo version 2.8.4, Please check registry access list (whitelist/blacklist).
1.原因分析:
以上异常意思是当前调用者被禁止访问某个服务,请检查下注册中心访问列表,还有黑名单和白名单。
其实线下环境根本没有对服务做白名单和黑名单机制,因为线下环境给开发人员的账号是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;}
2.结论:
UrlsWithoutEmpty的结果是空或者size为0,则强制返回一个protocol为empty的url,看来源头就在这里了。传入的List<String> providers实际上就是最新的服务提供者信息,当某个服务没有任何provider时,providers就变为一个size为o的List了,导致返回一个协议头为empty的url,进而导致forbidden为true,屏蔽了consumer调用。
3.解决办法:
重启服务器,重新运行,访问你的url