dubbo源码分析7-- 注册中心Zookeeper

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

这里写图片描述

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<String, Registry> REGISTRIES = new 
       ConcurrentHashMap<String, Registry>();

   /**
    * 获取所有注册中心
    * 
    * @return 所有注册中心
    */
   public static Collection<Registry> 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
   &timestamp=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<URL> failedRegistered = new ConcurrentHashSet<URL>();
    //取消注册失败
    private final Set<URL> failedUnregistered = new ConcurrentHashSet<URL>();
    //订阅失败
    private final ConcurrentMap<URL, Set<NotifyListener>> failedSubscribed
      = new ConcurrentHashMap<URL, Set<NotifyListener>>();
    //取消订阅失败
    private final ConcurrentMap<URL, Set<NotifyListener>> failedUnsubscribed 
    = new ConcurrentHashMap<URL, Set<NotifyListener>>();
    //通知失败
    private final ConcurrentMap<URL, Map<NotifyListener, List<URL>>> 
      failedNotified = new ConcurrentHashMap<URL, Map<NotifyListener, 
      List<URL>>>();

     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<URL> urls = entry.getValue();
           listener.notify(urls);
      }

    }        
}

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

public class ZookeeperRegistry extends FailbackRegistry {

    private final String        root;

    private final Set<String> anyServices = new ConcurrentHashSet<String>();

    private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, 
        ChildListener>> zkListeners = new 
        ConcurrentHashMap<URL, ConcurrentMap<NotifyListener, 
        ChildListener>>();
    //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<URL> registered = new ConcurrentHashSet<URL>();
 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

  • 通知

3) 出现异常,根据url从本地缓存文件中获取到提供者的地址,通知

  • AbstractRegistry
    保存订阅信息到集合中
//订阅
private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = 
new ConcurrentHashMap<URL, Set<NotifyListener>>();

 public void subscribe(URL url, NotifyListener listener) {
    Set<NotifyListener> listeners = subscribed.get(url);
     if (listeners == null) {
         subscribed.putIfAbsent(url, 
         new ConcurrentHashSet<NotifyListener>());
         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<URL> 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的子节点监听器,就创建子节点监听器。一旦子节点发生改变,那么就通知
    2) 创建 /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<URL, ConcurrentMap<NotifyListener,
  ChildListener>> zkListeners = new ConcurrentHashMap<URL,
   ConcurrentMap<NotifyListener, ChildListener>>();


protected void doSubscribe(final URL url, final NotifyListener listener) {
    //接口名称(*代表需要监听root下面的所有节点)
    if ("*".equals(url.getServiceInterface())) {
        ConcurrentMap<NotifyListener, ChildListener> 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<String> 
                    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<String> 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<URL> urls = new ArrayList<URL>();
        /**将url转变成
            /dubbo/com.test.ITestService/providers
            /dubbo/com.test.ITestService/configurators
            /dubbo/com.test.ITestService/routers
        **/
        for (String path : toCategoriesPath(url)) {
            ConcurrentMap<NotifyListener, ChildListener> 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<String> 
                        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<String> 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<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
        List<URL> 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<URL> urls) {
     try {
        doNotify(url, listener, urls);
     } catch (Exception t) {
         // 将失败的通知请求记录到失败列表,定时重试..
         Map<NotifyListener, List<URL>> 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…

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

    3) 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<URL> urls) {
    Map<String, List<URL>> result = new HashMap<String, List<URL>>();
    //根据url中的category分割    
    Map<String, List<URL>> categoryNotified = notified.get(url);
    //空的话就创建并且放入到map

    for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
        String category = entry.getKey();
        List<URL> categoryList = entry.getValue();
        //将url写入到本地缓存中
        saveProperties(url);
        //监听器通知url
        listener.notify(categoryList);
    }
}

保存到本地缓存文件

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

private void saveProperties(URL url) {
    try {
        //根据url取出所有的地址
        StringBuilder buf = new StringBuilder();
        Map<String, List<URL>> categoryNotified = notified.get(url);
        if (categoryNotified != null) {
            for (List<URL> 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<URL> urls) {
    //providers
    List<URL> invokerUrls = new ArrayList<URL>();
    //router
    List<URL> routerUrls = new ArrayList<URL>();
    //configurator
    List<URL> configuratorUrls = new ArrayList<URL>();
    //循环url,根据category放入到对应的集合中...

    // 处理configurators,去掉empty
    if (configuratorUrls != null && configuratorUrls.size() >0 ){
        this.configurators = toConfigurators(configuratorUrls);
    }
    // 处理routers,去掉empty
    if (routerUrls != null && routerUrls.size() >0 ){
        List<Router> routers = toRouters(routerUrls);
        if(routers != null){ // null - do nothing
            setRouters(routers);
        }
    }

    List<Configurator> 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<URL> 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<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap;
       // 将URL列表转成Invoker列表
       Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;
       // 换方法名映射Invoker列表
       Map<String, List<Invoker<T>>> 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<T> invoker = new DubboInvoker<T>
     (serviceType, url, getClients(url), invokers);
 */
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
    Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, 
        Invoker<T>>();
    if(urls == null || urls.size() == 0){
        return newUrlInvokerMap;
    }
    Set<String> keys = new HashSet<String>();
    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<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; 
        Invoker<T> invoker = localUrlInvokerMap == null ? null : 
            localUrlInvokerMap.get(key);
        // 缓存中没有,重新refer  
        if (invoker == null) { 
            boolean enabled = true;
            //根据url获取参数disabled或enabled
            if (enabled) {
                //重新refer
                invoker = new InvokerDelegete<T>(
                    protocol.refer(serviceType, url), url, providerUrl);
            }
            if (invoker != null) { // 将新的引用放入缓存
                newUrlInvokerMap.put(key, invoker);
            }
        }else {
            newUrlInvokerMap.put(key, invoker);
        }
    }
    keys.clear();
    return newUrlInvokerMap;
}

猜你喜欢

转载自blog.csdn.net/liyue1090041509/article/details/79994604
今日推荐