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

你的名字 2021-04-12 14:54 944阅读 0赞

业务方在使用时经常会出现如下错误

170150\_BgS5\_1262062.png

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

170416\_rpCq\_1262062.png

一般都能解决问题

今天我们通过源代码来进行一下简单的分析,先来看一下代码的UML图

170536\_wLxx\_1262062.png

异常出错的地方是在RegistryDirectory,此类实现了NotifyListener接口,该接口中有一个很重要的方法就是notify,是处理zk的watch事件的,当consumer端监听的节点发生数据变化时,会通知consumer端进行响应的处理,下面我们大概浏览一下代码

  1. //处理zk的watch事件
  2. public synchronized void notify(List<URL> urls) {
  3. List<URL> invokerUrls = new ArrayList<URL>();
  4. List<URL> routerUrls = new ArrayList<URL>();
  5. List<URL> configuratorUrls = new ArrayList<URL>();
  6. for (URL url : urls) {
  7. String protocol = url.getProtocol();
  8. String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
  9. if (Constants.ROUTERS_CATEGORY.equals(category)
  10. || Constants.ROUTE_PROTOCOL.equals(protocol)) {
  11. routerUrls.add(url);
  12. } else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
  13. || Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
  14. configuratorUrls.add(url);
  15. } else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
  16. invokerUrls.add(url);
  17. } else {
  18. logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
  19. }
  20. }
  21. // configurators
  22. if (configuratorUrls != null && configuratorUrls.size() >0 ){
  23. this.configurators = toConfigurators(configuratorUrls);
  24. }
  25. // routers
  26. if (routerUrls != null && routerUrls.size() >0 ){
  27. List<Router> routers = toRouters(routerUrls);
  28. if(routers != null){ // null - do nothing
  29. setRouters(routers);
  30. }
  31. }
  32. List<Configurator> localConfigurators = this.configurators; // local reference
  33. // 合并override参数
  34. this.overrideDirectoryUrl = directoryUrl;
  35. if (localConfigurators != null && localConfigurators.size() > 0) {
  36. for (Configurator configurator : localConfigurators) {
  37. this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
  38. }
  39. }
  40. // providers
  41. refreshInvoker(invokerUrls);
  42. }
  43. /**
  44. * 根据invokerURL列表转换为invoker列表。转换规则如下:
  45. * 1.如果url已经被转换为invoker,则不在重新引用,直接从缓存中获取,注意如果url中任何一个参数变更也会重新引用
  46. * 2.如果传入的invoker列表不为空,则表示最新的invoker列表
  47. * 3.如果传入的invokerUrl列表是空,则表示只是下发的override规则或route规则,需要重新交叉对比,决定是否需要重新引用。
  48. * @param invokerUrls 传入的参数不能为null
  49. */
  50. private void refreshInvoker(List<URL> invokerUrls){
  51. if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
  52. && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
  53. this.forbidden = true; // 禁止访问
  54. this.methodInvokerMap = null; // 置空列表
  55. destroyAllInvokers(); // 关闭所有Invoker
  56. } else {
  57. this.forbidden = false; // 允许访问
  58. Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
  59. if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
  60. invokerUrls.addAll(this.cachedInvokerUrls);
  61. } else {
  62. this.cachedInvokerUrls = new HashSet<URL>();
  63. this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
  64. }
  65. if (invokerUrls.size() ==0 ){
  66. return;
  67. }
  68. Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表
  69. Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
  70. // state change
  71. //如果计算错误,则不进行处理.
  72. if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
  73. logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
  74. return ;
  75. }
  76. this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
  77. this.urlInvokerMap = newUrlInvokerMap;
  78. try{
  79. destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
  80. }catch (Exception e) {
  81. logger.warn("destroyUnusedInvokers error. ", e);
  82. }
  83. }
  84. }

下面来看一下EMPTY URL是哪里来的

代码ZookeeperRegistry.java

173728\_GedP\_1262062.png

  1. private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
  2. List<URL> urls = toUrlsWithoutEmpty(consumer, providers);
  3. if (urls == null || urls.isEmpty()) {
  4. int i = path.lastIndexOf('/');
  5. String category = i < 0 ? path : path.substring(i + 1);
  6. //这里会添加empty url
  7. URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category);
  8. urls.add(empty);
  9. }
  10. return urls;
  11. }
  12. private List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
  13. List<URL> urls = new ArrayList<URL>();
  14. if (providers != null && providers.size() > 0) {
  15. for (String provider : providers) {
  16. provider = URL.decode(provider);
  17. if (provider.contains("://")) {
  18. URL url = URL.valueOf(provider);
  19. if (UrlUtils.isMatch(consumer, url)) {
  20. urls.add(url);
  21. }
  22. }
  23. }
  24. }
  25. return urls;
  26. }
  27. //关键是这里会进行URL的过滤
  28. public static boolean isMatch(URL consumerUrl, URL providerUrl) {
  29. //比较接口
  30. String consumerInterface = consumerUrl.getServiceInterface();
  31. String providerInterface = providerUrl.getServiceInterface();
  32. if( ! (Constants.ANY_VALUE.equals(consumerInterface) || StringUtils.isEquals(consumerInterface, providerInterface)) ) return false;
  33. //比较目录(这里是在哪里用到的,占时还没有搞懂)
  34. if (! isMatchCategory(providerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY),
  35. consumerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY))) {
  36. return false;
  37. }
  38. if (! providerUrl.getParameter(Constants.ENABLED_KEY, true)
  39. && ! Constants.ANY_VALUE.equals(consumerUrl.getParameter(Constants.ENABLED_KEY))) {
  40. return false;
  41. }
  42. String consumerGroup = consumerUrl.getParameter(Constants.GROUP_KEY);
  43. String consumerVersion = consumerUrl.getParameter(Constants.VERSION_KEY);
  44. String consumerClassifier = consumerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
  45. String providerGroup = providerUrl.getParameter(Constants.GROUP_KEY);
  46. String providerVersion = providerUrl.getParameter(Constants.VERSION_KEY);
  47. String providerClassifier = providerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE);
  48. //group,version,classifier
  49. return (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
  50. && (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
  51. && (consumerClassifier == null || Constants.ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));
  52. }

这里过滤完后,会添加一个empty url,然后就会将forbidden关键字至为true,后面客户方调用时,就会直接抛出异常,提示不可用

发表评论

表情:
评论列表 (有 0 条评论,944人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Collectionlist分析

    Java中常用到ArrayList和LinkedList,面试中也常问到两者的区别,各自的使用场景。要想清楚的明白他们的区别,那还是得从源码入手。 List接口 Lis