dubbo-- 注册中心Zookeeper (注册、订阅、通知)

注册中心可以是zookeeper、redis和dubbo

dubbo-- 注册中心Zookeeper (注册、订阅、通知)_第1张图片

zookeeper的路径如上图所示,root下面有接口,接口下面有providers和consumers。
首先会注册节点.

消费者会订阅接口下的providers的所有子节点。一旦providers下的子节点发生改变,就会通知消息给消费者。
而监控中心订阅的是接口。

其中接口下会有四个子节点providers, consumers, routers, configurators

其中dubbo、接口、providers都是持久化节点,只有url是临时节点。当会话消失(服务器断开与zookeeper的连接),对应的临时节点会被删除。(利用zookeeper中的临时节点特性以及watch)

AbstractRegistryFactory

AbstractRegistryFactory 这个主要的目的是在获取注册中心时

  1. 加锁
  2. 记录注册中心集合.
public abstract class AbstractRegistryFactory implements RegistryFactory {

   // 注册中心获取过程锁
   private static final ReentrantLock LOCK = new ReentrantLock();

   // 注册中心集合  注册中心地址-- Registry 
   private static final Map REGISTRIES = new 
	   ConcurrentHashMap();

   /**
    * 获取所有注册中心
    * 
    * @return 所有注册中心
    */
   public static Collection getRegistries() {
       return Collections.unmodifiableCollection(REGISTRIES.values());
   }

  
   public Registry getRegistry(URL url) {
   /**
   zookeeper://10.118.22.25:2181/com.alibaba.dubbo.registry.RegistryService?
   application=testservice&dubbo=2.8.4&interface=
   com.alibaba.dubbo.registry.RegistryService&logger=slf4j&pid=6668
   ×tamp=1524045346142
**/
   	url = url.setPath(RegistryService.class.getName())
   			.addParameter(Constants.INTERFACE_KEY,
   			 RegistryService.class.getName())
   			.removeParameters(Constants.EXPORT_KEY, 
   			Constants.REFER_KEY);
   /**zookeeper://10.118.22.25:2181/com.alibaba.dubbo.registry.
    RegistryService**/
   	String key = url.toServiceString();
       // 锁定注册中心获取过程,保证注册中心单一实例
       LOCK.lock();
       try {
           Registry registry = REGISTRIES.get(key);
           if (registry != null) {
               return registry;
           }
           registry = createRegistry(url);
           if (registry == null) {
               throw new IllegalStateException("...");
           }
           REGISTRIES.put(key, registry);
           return registry;
       } finally {
           // 释放锁
           LOCK.unlock();
       }
   }
 /**
    * 关闭所有已创建注册中心
    */
   public static void destroyAll() {
       // 锁定注册中心关闭过程
       LOCK.lock();
       try {
           for (Registry registry : getRegistries()) {
               try {
                   registry.destroy();
               } catch (Throwable e) {
                   LOGGER.error(e.getMessage(), e);
               }
           }
           REGISTRIES.clear();
       } finally {
           // 释放锁
           LOCK.unlock();
       }
   }

//创建注册中心
   protected abstract Registry createRegistry(URL url);

}

ZookeeperRegistry##

ZookeeperRegistryFactory提供生成ZookeeperRegistry对象.
属于多继承
ZookeeperRegistry – >FailbackRegistry --> AbstractRegistry

初始化:
1) 创建本地缓存文件,从缓存中读取到property中
2) 启动定时器,去重新注册、订阅等操作
3) 初始化zookeeper的客户端(zkClient)


AbstractRegistry构造函数
AbstractRegistry:主要是缓存注册中心里的地址到本地文件中

public abstract class AbstractRegistry implements Registry {
	 //本地磁盘缓
	private final Properties properties = new Properties();
 // 本地磁盘缓存文件
    private File file;
	public AbstractRegistry(URL url) {
	   this.registryUrl = url;
	  // 启动文件保存定时器
	   syncSaveFile = url.getParameter("save.file", false);
	     /**C:\Users\pc/.dubbo/dubbo-registry-10.118.22.25.cache
	      主机名+.dubbo/dubbo-registry-注册中心ip+.cache
	     **/
	   String filename = url.getParameter("file", 
		      System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + 
		       url.getHost() + ".cache");
	   File file = null;
       if (ConfigUtils.isNotEmpty(filename)) {
          file = new File(filename);
          //如果目录不存在,就创建目录 ..
       }
	   this.file = file;      
	}
	  //将缓存文件载入到properties(注册中心zookeeper中的提供者接口)
	 
   
}

FailbackRegistry
定时对失败的进行重试,主要针对于以下几种:
1) 注册失败
2)取消注册失败
3)订阅失败
4)取消订阅失败
5)通知失败

public abstract class FailbackRegistry extends AbstractRegistry {
 // 失败重试定时器,定时检查是否有请求失败,如有,无限次重试
    private final ScheduledFuture retryFuture;
    //注册失败
    private final Set failedRegistered = new ConcurrentHashSet();
	//取消注册失败
    private final Set failedUnregistered = new ConcurrentHashSet();
	//订阅失败
    private final ConcurrentMap> failedSubscribed
      = new ConcurrentHashMap>();
	//取消订阅失败
    private final ConcurrentMap> failedUnsubscribed 
    = new ConcurrentHashMap>();
	//通知失败
    private final ConcurrentMap>> 
      failedNotified = new ConcurrentHashMap>>();
      
	 public FailbackRegistry(URL url) {
	    super(url);
	    int retryPeriod = url.getParameter("retry.period",5 * 1000);
	    //启动定时器进行重连注册中心
	    this.retryFuture=retryExecutor.scheduleWithFixedDelay(new 
	        Runnable() {
	        public void run() {
	            // 检测并连接注册中心
	            retry();
	        }
	    }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
	}

     //重试失败的动作 (如果中间出现异常,忽略等待重试)
    protected void retry() {
	  //注册    
      if (! failedRegistered.isEmpty()) {
           //遍历failedRegistered并且从中删除
           doRegister(url);
      }
      //取消注册
      if(! failedUnregistered.isEmpty()) {
	      doUnregister(url);
      }
      //订阅
      if (! failedSubscribed.isEmpty()) {
	       doSubscribe(url, listener);
      }
      //取消订阅
      if (! failedUnsubscribed.isEmpty()) {
		   doUnsubscribe(url, listener);
      }
      //通知
      if (! failedNotified.isEmpty()) {
		   NotifyListener listener = entry.getKey();
           List urls = entry.getValue();
           listener.notify(urls);
      }
      
    }        
}

**ZookeeperRegistry **
主要处理Zookeeper,客户端有两种,Curator和ZkClient,默认是ZkClient

public class ZookeeperRegistry extends FailbackRegistry {
	
    private final String        root;
    
    private final Set anyServices = new ConcurrentHashSet();

    private final ConcurrentMap> zkListeners = new 
	    ConcurrentHashMap>();
    //zoookeeper客户端(默认是zkclient)
    private final ZookeeperClient zkClient;

	public ZookeeperRegistry(URL url, ZookeeperTransporter
		 zookeeperTransporter) {
        super(url);
		//根节点,默认是/dubbo
        this.root = group;
        //zookeeperTransporter客户端有Curator和ZkClient,默认是ZkClient
        zkClient = zookeeperTransporter.connect(url);
        //监听如果重连了,那么
        zkClient.addStateListener(new StateListener() {
            public void stateChanged(int state) {
            	if (state == RECONNECTED) {
	            	try {
	            	    //将所有注册的地址放入到注册失败的集合中
	            	    //将所有订阅的地址放入到订阅失败的集合中
						recover();
					} catch (Exception e) {
						logger.error(e.getMessage(), e);
					}
            	}
            }
        });
    }
}

注册##

消费者或者提供者在暴露服务或者引用服务时,会往zookeeper上注册节点。

主要做了以下几步:
1)记录注册注册地址
2) 注册节点到zookeeper上
3) 捕捉错误信息,出错则记录下来,等待定期器去重新执行

  • AbstractRegistry
    记录地址
private final Set registered = new ConcurrentHashSet();
 public void register(URL url) {
    registered.add(url);
}


  • FailbackRegistry
    主要是为了捕捉注册时是否失败,失败记录到集合中
public abstract class FailbackRegistry extends AbstractRegistry {
	public void register(URL url) {
        super.register(url);
        //从注册失败集合中删除
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
        try {
            // 向服务器端发送注册请求
            doRegister(url);
        } catch (Exception e) {
            Throwable t = e;
            // 如果开启了启动时检测,则直接抛出异常....
            // 将失败的注册请求记录到失败列表,定时重试
            failedRegistered.add(url);
        }
    }
}
  • ZookeeperRegistry
public class ZookeeperRegistry extends FailbackRegistry {
   protected void doRegister(URL url) {
      /**
      /consumer://10.118.14.204/com.test.ITest?application=ec-service-impl..
      
      向zookeeper中注册临时文件节点/dubbo/com.test.ITest/consumers,
      值就是consumer://10.118.14.204/com.test.ITest?application=..
      /dubbo/com.test.ITest/consumers
      */
      zkClient.create(toUrlPath(url), url.getParameter("dynamic", true));
   }
}

订阅

消费者在引用服务时,会订阅接口下的providers的节点。一旦providers下的子节点发生改变(提供者的服务器增加或者删除),会通知到消费者。消费者会把提供者的集群地址缓存到本地。

主要做了以下几步操作(以具体接口为例)

  1. 将订阅信息记录到集合中
  2. 将路径转变成/dubbo/xxService/providers,/dubbo/xxService/configurators
    ,/dubbo/xxService/routers 循环这三个路径
  • 如果消费者的接口没有创建过子节点监听器,那么就创建子节点监听器

  • 创建路径节点,并将子节点监听器放入到节点上。(一旦子节点发生改变,就通知)

  • 获取到当前路径节点下的所有子节点(提供者),将这些子节点组装成集合,如果没有节点,那么就将消费者的地址的协议变成empty

    empty://10.118.14.204/com.test.ITestService?application=testservice&category=configurators

  • 通知

  1. 出现异常,根据url从本地缓存文件中获取到提供者的地址,通知
  • AbstractRegistry
    保存订阅信息到集合中
//订阅
private final ConcurrentMap> subscribed = 
new ConcurrentHashMap>();

 public void subscribe(URL url, NotifyListener listener) {
    Set listeners = subscribed.get(url);
     if (listeners == null) {
         subscribed.putIfAbsent(url, 
         new ConcurrentHashSet());
         listeners = subscribed.get(url);
     }
     listeners.add(listener);
 }
  • FailbackRegistry
    出现异常时,会根据本地缓存文件取的url并通知.
public void subscribe(URL url, NotifyListener listener) {
        super.subscribe(url, listener);
        removeFailedSubscribed(url, listener);
        try {
            // 向服务器端发送订阅请求
            doSubscribe(url, listener);
        } catch (Exception e) {
            Throwable t = e;
            List urls = getCacheUrls(url);
            if (urls != null && urls.size() > 0) {
                notify(url, listener, urls);
            } 
            // 将失败的订阅请求记录到失败列表,定时重试
            addFailedSubscribed(url, listener);
        }
    }
  • ZookeeperRegistry
    接口是 (对所有的接口进行订阅,有点类似于递归订阅)*

    1. 如果在集合中没有创建过*的子节点监听器,那么就创建子节点监听器,一旦root下的子节点(service)发生改变,那么就对这个节点就行订阅NotifyListener 。(这时就有具体的接口了)
    2. 创建root节点,将子节点监听器放入到root上。并返回root下的所有的接口,对这些接口订阅NotifyListener

    接口是具体 (以providers为例)
    1)将接口名称转变成/dubbo/com.test.ITestService/providers,集合中没有没有providers的子节点监听器,就创建子节点监听器。一旦子节点发生改变,那么就通知

    1. 创建 /dubbo/com.test.ITestService/providers,并且将子节点监听器放入到这个节点上,并返回所有的子节点(提供者),通知。

总结
当消费者要订阅接口中的提供者时
会监听/dubbo/xxService/providers下的所有提供者。一旦提供者的节点删除或增加时,都会通知到消费者的url(consumer://10.118.14.204/com.test.ITestService…)

它会监听以下三个节点的子节点
1) /dubbo/xxService/providers
2)/dubbo/xxService/configurators
3)/dubbo/xxService/routers

组装的url集合(即提供者的子节点providers,configurators,routers下的子节点)。如果没有子节点(没有提供者),那么就将消费者的协议变成empty作为url。

 //存放子节点的监听器
 private final ConcurrentMap> zkListeners = new ConcurrentHashMap>();
   

protected void doSubscribe(final URL url, final NotifyListener listener) {
	//接口名称(*代表需要监听root下面的所有节点)
    if ("*".equals(url.getServiceInterface())) {
        ConcurrentMap listeners = zkListeners.
        get(url);
        //如果listeners为空创建并放入到map中...
        ChildListener zkListener = listeners.get(listener);
        /**
           root下的子节点是service接口
           创建子节点监听器,对root下的子节点做监听,一旦有子节点发生改变,
           那么就对这个节点进行订阅.
        **/
        if (zkListener == null) {
            listeners.putIfAbsent(listener, new ChildListener() {
                public void childChanged(String parentPath, List 
	                currentChilds) {
                    for (String child : currentChilds) {
						//如果不存在,才订阅
                        if (! anyServices.contains(child)) {
                            anyServices.add(child);
                            //订阅
                            subscribe(url.setPath(child).addParameters(
                            "interface", child,"check", "false"), listener);
                        }
                    }
                }
            });
            zkListener = listeners.get(listener);
        }
        //创建root节点
        zkClient.create(root, false);
        //添加root节点的子节点监听器,并返回当前的services
        List services = zkClient.addChildListener(root, zkListener);
        if (services != null && services.size() > 0) {
		    //对root下的所有service节点进行订阅
            for (String service : services) {
				service = URL.decode(service);
				anyServices.add(service);
                subscribe(url.setPath(service).addParameters("interface", 
                service, "check", "false"), listener);
            }
        }
    } else {
        List urls = new ArrayList();
        /**将url转变成
	        /dubbo/com.test.ITestService/providers
	        /dubbo/com.test.ITestService/configurators
	        /dubbo/com.test.ITestService/routers
        **/
        for (String path : toCategoriesPath(url)) {
            ConcurrentMap listeners = 
	            zkListeners.get(url);
	        //如果listeners为空就创建并放入盗map中
            ChildListener zkListener = listeners.get(listener);
            /**
              对接口下的providers的子节点进行监听,一旦发生改变,就通知
            **/
            if (zkListener == null) {
                listeners.putIfAbsent(listener, new ChildListener() {
                    public void childChanged(String parentPath, List 
	                    currentChilds) {
	                    //通知
                    	ZookeeperRegistry.this.notify(url, listener, 
	                    	toUrlsWithEmpty(url, parentPath, currentChilds));
                    }
                });
                zkListener = listeners.get(listener);
            }
            //创建/dubbo/com.test.ITestService/providers
            zkClient.create(path, false);
            //获取到providers的所有子节点(提供者)
            List children = zkClient.addChildListener(path, 
	            zkListener);
	        //获取到所有的提供者,组装起来
            if (children != null) {
                //有子节点组装,没有那么就将消费者的协议变成empty作为url。
            	urls.addAll(toUrlsWithEmpty(url, path, children));
            }
        }
        //通知/dubbo/com.test.ITestService/providers的所有子节点
        notify(url, listener, urls);
    }
       
 }

/**
根据url获取到哪些类型
consumer://10.118.14.204/com.test.ITestService?application=testservice
&category=providers,configurators,routers&...
这里的category是重点
**/
private String[] toCategoriesPath(URL url) {
    String[] categroies;
    //如果是*
    if ("*".equals(url.getParameter(Constants.CATEGORY_KEY))) 
        categroies = new String[] {"providers", "consumers", 
            "routers", "configurators"}; 
    else
    //从url获取到category的值,没有的话就默认providers
        categroies = url.getParameter("category", 
	        new String[] {"providers"});
    String[] paths = new String[categroies.length];
    //将格式转变成/dubbo/xxService/类型
    for (int i = 0; i < categroies.length; i ++) {
        paths[i] = toServicePath(url) + "/" + categroies[i];
    }
    return paths;
}

/**
组装providers、routers、configurators下的url。
如果有提供者那么就组装;没有的话,就将消费者的协议变成empty
**/
private List toUrlsWithEmpty(URL consumer, String path, List providers) {
        List urls = toUrlsWithoutEmpty(consumer, providers);
        if (urls.isEmpty()) {
        	int i = path.lastIndexOf('/');
        	String category = i < 0 ? path : path.substring(i + 1);
        	URL empty = consumer.setProtocol("empty").addParameter(
	        	"category", category);
            urls.add(empty);
        }
        return urls;
    }



通知

有三个参数
url: 消费者的地址 consumer://10.118.14.204/com…
listener: 监听器
urls: providers,configurators和routers

1)写入到本地缓存文件中
文件名称:
2) 监听器通知

  • FailbackRegistry
    主要是铺捉到异常时放入到集合中,定时重试
 protected void notify(URL url, NotifyListener listener, List urls) {
     try {
     	doNotify(url, listener, urls);
     } catch (Exception t) {
         // 将失败的通知请求记录到失败列表,定时重试..
         Map> listeners = failedNotified.get(url);
     }
 }
  • AbstractRegistry

    urls三种
    1) providers(providers下的子节点)
    dubbo://10.118.22.29:20710/com.test.ITestService?anyhost=true&application=testservice&default.cluster=failfast…

    1. configurators(configurators下的子节点为空,将消费者的url变成empty )
      empty://10.118.14.204/com.test.ITestService?application=testservice&category=configurators&default.check=false…

    2. routers(routers下的子节点为空,将消费者的url变成empty)
      empty://10.118.14.204/com.test.ITestService?application=testservice&category=routers&default.check=false…

protected void notify(URL url, NotifyListener listener, List urls) {
    Map> result = new HashMap>();
    //根据url中的category分割    
    Map> categoryNotified = notified.get(url);
	//空的话就创建并且放入到map
	
    for (Map.Entry> entry : result.entrySet()) {
        String category = entry.getKey();
        List categoryList = entry.getValue();
		//将url写入到本地缓存中
        saveProperties(url);
        //监听器通知url
        listener.notify(categoryList);
    }
}

保存到本地缓存文件

组装url保存到properties中,如果是同步,直接保存到本地缓存文件中,否则文件缓存定时写入

private void saveProperties(URL url) {
    try {
	    //根据url取出所有的地址
        StringBuilder buf = new StringBuilder();
        Map> categoryNotified = notified.get(url);
        if (categoryNotified != null) {
            for (List us : categoryNotified.values()) {
                for (URL u : us) {
                    if (buf.length() > 0) {
                        buf.append(URL_SEPARATOR);
                    }
                    buf.append(u.toFullString());
                }
            }
        }
        //放入到properties中
        properties.setProperty(url.getServiceKey(), buf.toString());
        long version = lastCacheChanged.incrementAndGet();
        if (syncSaveFile) {
	        //直接保存文件缓存
            doSaveProperties(version);
        } else {
	        //文件缓存定时写入
            registryCacheExecutor.execute(new SaveProperties(version));
        }
    } catch (Throwable t) {
        logger.warn(t.getMessage(), t);
    }
}

通过 AtomicLong 来控制锁
首先会有个dubbo-registry-10.118.22.25.cache.lock,会获取这个文件的锁,然后保存dubbo-registry-10.118.22.25.cache文件,再释放锁。

public void doSaveProperties(long version) {
   if(version < lastCacheChanged.get()){
       return;
   }
   Properties newProperties = new Properties();
   // 保存之前先读取一遍file,防止多个注册中心之间冲突...
       
// 保存
   try {
	 newProperties.putAll(properties);
     //首先会有个空文件dubbo-registry-10.118.22.25.cache.lock
     File lockfile = new File(file.getAbsolutePath() + ".lock");
     if (!lockfile.exists()) {
     	 lockfile.createNewFile();
     }
     RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
     try {
         FileChannel channel = raf.getChannel();
         try {
          //获取到lock文件的锁  
          FileLock lock = channel.tryLock();
          try {
          	// 将Properties保存到file中
          } finally {
          	lock.release();
          }
         } finally {
             channel.close();
         }
     } finally {
         raf.close();
     }
 } catch (Throwable e) {
	 //出错了再重新保存
     if (version < lastCacheChanged.get()) {
         return;
     } else {
         registryCacheExecutor.execute(new SaveProperties
         (lastCacheChanged.incrementAndGet()));
     }
 }
}

监听器通知 (当收到服务变更通知时触发。)##

当收到提供者的地址发生改变时,这时刷新缓存中的invoker,如果url不存在,那么重新refer(根据dubbo协议)

通知需处理契约:
1. 总是以服务接口和数据类型为维度全量通知,即不会通知一个服务的同类型的部分数据,用户不需要对比上一次通知结果。
2. 订阅时的第一次通知,必须是一个服务的所有类型数据的全量通知。
3. 中途变更时,允许不同类型的数据分开通知,比如:providers, consumers, routers, overrides,允许只通知其中一种类型,但该类型的数据必须是全量的,不是增量的。
4. 如果一种类型的数据为空,需通知一个empty协议并带category参数的标识性URL数据。
5. 通知者(即注册中心实现)需保证通知的顺序,比如:单线程推送,队列串行化,带版本对比。

  • RegistryDirectory
 public synchronized void notify(List urls) {
	//providers
    List invokerUrls = new ArrayList();
    //router
    List routerUrls = new ArrayList();
    //configurator
    List configuratorUrls = new ArrayList();
    //循环url,根据category放入到对应的集合中...
    
    // 处理configurators,去掉empty
    if (configuratorUrls != null && configuratorUrls.size() >0 ){
        this.configurators = toConfigurators(configuratorUrls);
    }
    // 处理routers,去掉empty
    if (routerUrls != null && routerUrls.size() >0 ){
        List routers = toRouters(routerUrls);
        if(routers != null){ // null - do nothing
            setRouters(routers);
        }
    }
    
    List localConfigurators = this.configurators;
    // 合并override参数
    this.overrideDirectoryUrl = directoryUrl;
    if (localConfigurators != null && localConfigurators.size() > 0) {
        for (Configurator configurator : localConfigurators) {
            this.overrideDirectoryUrl = configurator.configure(
            overrideDirectoryUrl);
        }
    }
    // providers
    refreshInvoker(invokerUrls);
}

private void refreshInvoker(List invokerUrls){
	//如果只有一个empty,那么就禁止访问
   if (invokerUrls != null && invokerUrls.size() == 1 
    && invokerUrls.get(0) != null
    && "empty".equals(invokerUrls.get(0).getProtocol())) {
       this.forbidden = true; // 禁止访问
       this.methodInvokerMap = null; // 置空列表
       destroyAllInvokers(); // 关闭所有Invoker
   } else {
       this.forbidden = false; // 允许访问
       //...
       this.cachedInvokerUrls.addAll(invokerUrls);
       if (invokerUrls.size() ==0 ){
	       	return;
       }
       Map> oldUrlInvokerMap = this.urlInvokerMap;
       // 将URL列表转成Invoker列表
       Map> newUrlInvokerMap = toInvokers(invokerUrls) ;
       // 换方法名映射Invoker列表
       Map>> newMethodInvokerMap = 
        toMethodInvokers(newUrlInvokerMap); 
       // state change
       //如果计算错误,则不进行处理.
       if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
           return ;
       }
       this.methodInvokerMap = multiGroup ? 
       toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
       this.urlInvokerMap = newUrlInvokerMap;
       // 关闭未使用的Invoker
       destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap);
   }
}
/**
 * 将urls转成invokers,如果url已经被refer过,不再重新引用。
 * 如果没有那么需要refer
 * protocol.refer(serviceType, url)
	根据协议dubbo,所以他是DubboProtocol
	 DubboInvoker invoker = new DubboInvoker
	 (serviceType, url, getClients(url), invokers);
 */
private Map> toInvokers(List urls) {
    Map> newUrlInvokerMap = new HashMap>();
    if(urls == null || urls.size() == 0){
        return newUrlInvokerMap;
    }
    Set keys = new HashSet();
    String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
    for (URL providerUrl : urls) {
    	//如果reference端配置了protocol,则只选择匹配的protocol
        if ("empty".equals(providerUrl.getProtocol())) {
            continue;
        }
        //没有这个指定的协议,那么报错
        if (! ExtensionLoader.getExtensionLoader(Protocol.class).
	        hasExtension(providerUrl.getProtocol())) {
            continue;
        }
        //合并url参数 顺序为override > -D >Consumer > Provider
        URL url = mergeUrl(providerUrl);
        // URL参数是排序的
        String key = url.toFullString(); 
        // 重复URL,过滤
        
        Map> localUrlInvokerMap = this.urlInvokerMap; 
        Invoker invoker = localUrlInvokerMap == null ? null : 
	        localUrlInvokerMap.get(key);
	    // 缓存中没有,重新refer  
        if (invoker == null) { 
           	boolean enabled = true;
           	//根据url获取参数disabled或enabled
           	if (enabled) {
	           	//重新refer
           		invoker = new InvokerDelegete(
	           		protocol.refer(serviceType, url), url, providerUrl);
           	}
            if (invoker != null) { // 将新的引用放入缓存
                newUrlInvokerMap.put(key, invoker);
            }
        }else {
            newUrlInvokerMap.put(key, invoker);
        }
    }
    keys.clear();
    return newUrlInvokerMap;
}


你可能感兴趣的:(dubbo)