dubbo服务消费过程(二)

这篇博客详细阐述了Dubbo服务消费过程,包括服务的订阅和通知机制,RegistryDirectory如何动态更新Invoker列表,以及服务的列举和URL到Invoker的转换。重点讨论了客户端连接的建立,特别是DubboProtocol在服务提供者URL变化时触发的notify()方法,以及如何通过HeaderExchanger创建Netty客户端连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

服务的订阅和通知

Directory

在这里插入图片描述

Directory 继承自 Node 接口,Node 这个接口继承者比较多,像 Registry、Monitor、Invoker 等均继承了这个接口。这个接口包含了一个获取配置信息的方法 getUrl,实现该接口的类可以向外提供配置信息。另外,大家注意看 RegistryDirectory 实现了 NotifyListener 接口,当注册中心节点信息发生变化后,RegistryDirectory 可以通过此接口方法得到变更信息,并根据变更信息动态调整内部 Invoker 列表

在容错过程中(如FailoverClusterInvoker)会使用Directory#list来获取所有的invoke的列表。
模板模式

  • Directory顶层接口
  • AbstractDirectory封装了通用实现逻辑:list等
  • StaticDirectory Directory的静态列表实现,提供静态的Invoke列表,将传入的Invoker列表封装成静态的Directory对象,里面的列表不会改变,在ReferenceConfig#createProxy使用CLUSTER.join(new StaticDirectory(invokers))
  • RegistryDirectoryDirectory的动态列表实现提供动态的Invoke列表。会自动从注册中心更新Invoker列表,配置信息、路由信息。

RegistryDirectory
RegistryDirectory实现两点:
(1) 框架与注册中心的订阅,并动态更新本地Invoker列表、路由列表、配置信息的逻辑
(2) 实现父类的doList方法

订阅与动态更新主要方法subscribe、notify、refreshInvoker,辅助方法toConfigurators、toRouter

服务的订阅subscribe

注册中心ZK 的节点订阅和通知
调用链
RegistryProtocol#refer
-> RegistryProtocol#doRefer
-> directory#subscribe
-> registry#subscribe -->这个registry 是ZookeeperRegistry

ZookeeperRegistry父类FailbackRegistry#subscribe ->ZookeeperRegistry.doSubscribe()

会去监听下面的节点的路径的子节点变动

/dubbo/org.apache.dubbo.demo.DemoService/providers
/dubbo/org.apache.dubbo.demo.DemoService/configurators
/dubbo/org.apache.dubbo.de mo.DemoService/routers

directory#subscribe

订阅某个URL的更新信息。Dubbo在引用每个需要RPC调用(refer)Bean的时候,会调用directory.subscribe订阅这个bean的各种URL变化

public void subscribe(URL url) { 
	  // 设置consumerUrl 
      setConsumerUrl(url);
      // 把当前的RegistryDriectory作为listener,去监听zk节点的变化
      CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this);
      serviceConfigurationListener = new ReferenceConfigurationListener(this, url);
      registry.subscribe(url, this);
  }

FailbackRegistry#subscribe

移除失效的listener,调用doSubscribe进行订阅

// listener为RegistryDirectory
public void subscribe(URL url, NotifyListener listener) {
    super.subscribe(url, listener);
    // 移除失效的listener
    removeFailedSubscribed(url, listener); 
    try {
        // 调用ZookeeperRegistry.doSubscribe 实现 
        doSubscribe(url, listener);
    } catch (Exception e) {
        Throwable t = e;

        List<URL> urls = getCacheUrls(url);
        if (CollectionUtils.isNotEmpty(urls)) {
            notify(url, listener, urls);
            logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
        } else {
            // If the startup detection is opened, the Exception is thrown directly.
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true);
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }
        }
        // Record a failed registration request to a failed list, retry regularly
        addFailedSubscribed(url, listener); // 失败重试
    }
}

ZookeeperRegistry#doSubscribe

主要实现把所有Service层发起的订阅以及指定的Service层发起的订阅分开处理。

  • 所有Service层类似于监控中心发起的订阅。
  • 指定的Service层发起的订阅可以看作是服务消费者的订阅。
    @Override
    public void doSubscribe(final URL url, final NotifyListener listener) {
        try {
            if (ANY_VALUE.equals(url.getServiceInterface())) { // 这里主要用与服务端和zk连接 注册服务
               ... // 省略部分代码
            } else { // 消费端和zk连接 订阅节点 
                List<URL> urls = new ArrayList<>();
                for (String path : toCategoriesPath(url)) {
                    ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                    if (listeners == null) {
                       // 如果之前该路径没有添加过listener,则创建一个map来放置listener
                        zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
                        listeners = zkListeners.get(url);
                    }
                    ChildListener zkListener = listeners.get(listener);
                    if (zkListener == null) {
                       // 如果没有添加过对于子节点的listener,则创建,通知服务变化 回调NotifyListener
                        listeners.putIfAbsent(listener, (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
                        zkListener = listeners.get(listener);
                    }
                    zkClient.create(path, false);
                    //添加path节点的当前节点及子节点监听,并且获取子节点信息 
                    //也就是dubbo://ip:port/...
                    List<String> children = zkClient.addChildListener(path, zkListener);
                    if (children != null) {
                        urls.addAll(toUrlsWithEmpty(url, path, children));
                    }
                }
                // 调用notify进行通知,对已经可用的列表进行通知
                notify(url, listener, urls);
            }
        } catch (Throwable e) {
            throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

服务通知notify

FailbackRegistry.notify

调用FailbackRegistry.notify, 对参数进行判断。 然后调用AbstractRegistry.notify方法

 protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        try {
            doNotify(url, listener, urls);
        } catch (Exception t) {
            // Record a failed registration request to a failed list, retry regularly
            addFailedNotified(url, listener, urls);
            logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
        }
    }

 protected void doNotify(URL url, NotifyListener listener, List<URL> urls) {
     super.notify(url, listener, urls);
 }

AbstractRegistry.notify

protected void notify(URL url, NotifyListener listener, List<URL> urls) {
      if (url == null) {
          throw new IllegalArgumentException("notify url == null");
      }
      if (listener == null) {
          throw new IllegalArgumentException("notify listener == null");
      }
      if ((CollectionUtils.isEmpty(urls))
              && !ANY_VALUE.equals(url.getServiceInterface())) {
          logger.warn("Ignore empty notify urls for subscribe url " + url);
          return;
      }
      if (logger.isInfoEnabled()) {
          logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
      }
      // keep every provider's category.
      Map<String, List<URL>> result = new HashMap<>();
      for (URL u : urls) {
          if (UrlUtils.isMatch(url, u)) {
              String category = u.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
              List<URL> categoryList = result.computeIfAbsent(category, k -> new ArrayList<>());
              categoryList.add(u);
          }
      }
      if (result.size() == 0) {
          return;
      }
      // 会针对每一个category,调用listener.notify进行通知,然后更新本地的缓存文件
      Map<String, List<URL>> categoryNotified = notified.computeIfAbsent(url, u -> new ConcurrentHashMap<>());
      for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
          String category = entry.getKey();
          List<URL> categoryList = entry.getValue();
          categoryNotified.put(category, categoryList);
          listener.notify(categoryList); // 触发listener.notify  RegisteryDirectory.notify 
          saveProperties(url);
      }
  }

RegisteryDirectory.notify

接收服务变更通知, 监听到配置中心对应的URL变化,然后更新本地的配置参数。
在这里插入图片描述
(1) 新建三个list,分别用于保存更新的InvokerURL、路由配置URL、配置URL。遍历监听返回所有的url,分类放入三个list中
(2) 解析并更新配置参数:

  • 对于router类型参数,首先遍历所有的router类型URL,然后通过router工厂RouterFactory把每个URL包装成路由规则,最后更新本地的路由信息。这个过程会忽略以empty开头的URL
  • 对于Configurator类的参数,管理员可以在dubbo-admin动态配置功能上修改生产者的参数,这些参数会保存在配置中心的configurator类目录下。notify监听到的url配置变化,会解析并更新本地的Configurator配置
  • 对于Invoker类型的参数,如果是empty协议的URL,则会禁用该服务,并销毁本地缓存的Invoker;如果监听到的Invoker类型URL都是空的,则说明没有更新,直接使用本地的缓存;如果监听到的URL不为空,则把更新的URL和本地老的URL合并,创建新的Invoker,找出差异的老Invoker并销毁
    public synchronized void notify(List<URL> urls) {
    	//对url列表进行校验、过滤,然后分成 config、router、provider 3个分组map
        Map<String, List<URL>> categoryUrls = urls.stream()
                .filter(Objects::nonNull)
                .filter(this::isValidCategory)
                .filter(this::isNotCompatibleFor26x)
                .collect(Collectors.groupingBy(url -> {
                    if (UrlUtils.isConfigurator(url)) {
                        return CONFIGURATORS_CATEGORY;
                    } else if (UrlUtils.isRoute(url)) {
                        return ROUTERS_CATEGORY;
                    } else if (UrlUtils.isProvider(url)) {
                        return PROVIDERS_CATEGORY;
                    }
                    return "";
                }));
		// 定义配置器url的list
        List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
        // 将url转换成configurator
        this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);
		// 路由url
        List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());  
        // 将url转成rounter     
        toRouters(routerURLs).ifPresent(this::addRouters);
        // provider url
        List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
        /**
         * 3.x added for extend URL address
         */
        ExtensionLoader<AddressListener> addressListenerExtensionLoader = ExtensionLoader.getExtensionLoader(AddressListener.class);
        List<AddressListener> supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
        if (supportedListeners != null && !supportedListeners.isEmpty()) {
            for (AddressListener addressListener : supportedListeners) {
                providerURLs = addressListener.notify(providerURLs, getUrl(),this);
            }
        }
        // 刷新和覆盖 invokerlist
        refreshOverrideAndInvoker(providerURLs);
    }
    /**
     * 逐个调用注册中心里面的配置,覆盖原来的url,组成最新的url 放入overrideDirectoryUrl 存储
     * 根据 provider urls,重新刷新Invoker
     */
    private void refreshOverrideAndInvoker(List<URL> urls) {
        // mock zookeeper://xxx?mock=return null
        overrideDirectoryUrl();
        refreshInvoker(urls);
    }

服务overriderefresh

private volatile URL overrideDirectoryUrl;
private volatile URL registeredConsumerUrl;

overrideDirectoryUrl 的参数可以通过dubbo-admin上的服务参数重写
规则:override > -D >Consumer > Provider

RegistryDirectory .overrideDirectoryUrl

dubo-admin上更新路由规则或参数是通过override://协议实现的

private void overrideDirectoryUrl() {
      // merge override parameters
      this.overrideDirectoryUrl = directoryUrl;
      // 本地Configurator 重写
      List<Configurator> localConfigurators = this.configurators; // local reference
      doOverrideUrl(localConfigurators);
      List<Configurator> localAppDynamicConfigurators = CONSUMER_CONFIGURATION_LISTENER.getConfigurators(); // local reference
      // 消费端 重写
      doOverrideUrl(localAppDynamicConfigurators);
      if (serviceConfigurationListener != null) {
          List<Configurator> localDynamicConfigurators = serviceConfigurationListener.getConfigurators(); // local reference
          // 服务端的重写
          doOverrideUrl(localDynamicConfigurators);
      }
  }
  private void doOverrideUrl(List<Configurator> configurators) {
     if (CollectionUtils.isNotEmpty(configurators)) {
         for (Configurator configurator : configurators) {
             this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
         }
  }  

RegistryDirectory.refreshInvoker

refreshInvoker 方法是保证 RegistryDirectory 随注册中心变化而变化的关键所在

    private void refreshInvoker(List<URL> invokerUrls) {
        Assert.notNull(invokerUrls, "invokerUrls should not be null");
    	// invokerUrls 仅有一个元素,且 url 协议头为 empty,此时表示禁用所有服务
        if (invokerUrls.size() == 1
                && invokerUrls.get(0) != null
                && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
            // 设置 forbidden 为 true
            this.forbidden = true; // Forbid to access
            this.invokers = Collections.emptyList();
            routerChain.setInvokers(this.invokers);
            // 销毁所有 Invoker
            destroyAllInvokers(); // Close all invokers
        } else {
            this.forbidden = false; // Allow to access
            Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
            if (invokerUrls == Collections.<URL>emptyList()) {
                invokerUrls = new ArrayList<>();
            }
            if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
            	// 添加缓存 url 到 invokerUrls 中
                invokerUrls.addAll(this.cachedInvokerUrls);
            } else {
            	// 缓存 invokerUrls
                this.cachedInvokerUrls = new HashSet<>();
                this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
            }
            if (invokerUrls.isEmpty()) {
                return;
            }
            // 将 url 转成 Invoker
            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map

            if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
                logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls
                        .toString()));
                return;
            }

            List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
            // pre-route and build cache, notice that route cache should build on original Invoker list.
            // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
            // 重置路由invoker
            routerChain.setInvokers(newInvokers);
            // 合并多个组的Invoker
            //如果服务配置了分组,则把分组下的provider包装成StaticDirectory,组成一个 invoker 
            //实际上就是按照group进行合并
            this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
            this.urlInvokerMap = newUrlInvokerMap;

            try {
            	// 销毁无用 Invoker
                destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
            } catch (Exception e) {
                logger.warn("destroyUnusedInvokers error. ", e);
            }
        }
    }

服务的列举list

AbstractDirectory#list

 @Override
 public List<Invoker<T>> list(Invocation invocation) throws RpcException {
     if (destroyed) {
         throw new RpcException("Directory already destroyed .url: " + getUrl());
     }
   return doList(invocation);
 }

RegistryDirectory#dolist

列举Invoker,在AbstractDirectory#list中调用子类模板方法dolist

 @Override
  public List<Invoker<T>> doList(Invocation invocation) {
       if (forbidden) {
          // 服务提供者关闭或禁用了服务,此时抛出 No provider 异常
           // 1. No service provider 2. Service providers are disabled
           throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " +
                   getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +
                   NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() +
                   ", please check status of providers(disabled, not registered or in blacklist).");
       }
	  // 判断是否需要分组路由
       if (multiGroup) {// true  直接返回invokers 
           return this.invokers == null ? Collections.emptyList() : this.invokers;
       }
       List<Invoker<T>> invokers = null;
       try {
           // Get invokers from cache, only runtime routers will be executed.
           // 通过路由规则 选出 invokers
           invokers = routerChain.route(getConsumerUrl(), invocation);
       } catch (Throwable t) {
           logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
       }
       return invokers == null ? Collections.emptyList() : invokers;
   }

服务url->InvokertoInvokers

将服务的url转成invoker
RegistryDirectory#toInvokers

  private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
        Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
        if (urls == null || urls.isEmpty()) {
            return newUrlInvokerMap;
        }
        Set<String> keys = new HashSet<>();
        // 获取服务消费端配置的协议
        String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
        for (URL providerUrl : urls) {
            // If protocol is configured at the reference side, only the matching protocol is selected
            if (queryProtocols != null && queryProtocols.length() > 0) {
                boolean accept = false;
                String[] acceptProtocols = queryProtocols.split(",");
                // 检测服务提供者协议是否被服务消费者所支持
                for (String acceptProtocol : acceptProtocols) {
                    if (providerUrl.getProtocol().equals(acceptProtocol)) {
                        accept = true;
                        break;
                    }
                }
                if (!accept) {
                    // 若服务消费者协议头不被消费者所支持,则忽略当前 providerUrl
                    continue;
                }
            }
            // 忽略 empty 协议
            if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
                continue;
            }
            // 通过 SPI 检测服务端协议是否被消费端支持,不支持则抛出异常
            if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
                logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() +
                        " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() +
                        " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
                        ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
                continue;
            }
            // 合并url 规则:override > -D >Consumer > Provider
            URL url = mergeUrl(providerUrl);
            String key = url.toFullString(); // The parameter urls are sorted
            if (keys.contains(key)) { // Repeated url
                continue; // 忽略重复 url
            }
            keys.add(key);
            // 将本地 Invoker 缓存赋值给 localUrlInvokerMap
            Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
            // 获取与 url 对应的 Invoker
            Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
            if (invoker == null) { // 缓存未命中
                try {
                    boolean enabled = true;
                    if (url.hasParameter(DISABLED_KEY)) {
                        // 获取 disable 配置,取反,然后赋值给 enable 变量
                        enabled = !url.getParameter(DISABLED_KEY, false);
                    } else {
                        // 获取 enable 配置,并赋值给 enable 变量
                        enabled = url.getParameter(ENABLED_KEY, true);
                    }
                    if (enabled) {
                        // 调用 refer 获取 Invoker protocol-> xxxWrapper(DubboProtocol)
                        // InvokerDelegate(ProtocolFilterWrapper(ListenerInvokerWrapper(DubboInvoker())
                        invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
                    }
                } catch (Throwable t) {
                    logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
                }
                if (invoker != null) { // Put new invoker in cache
                   // 缓存 Invoker 实例
                    newUrlInvokerMap.put(key, invoker);
                }
            } else { // 缓存命中
               // 将 invoker 存储到 newUrlInvokerMap 中
                newUrlInvokerMap.put(key, invoker);
            }
        }
        keys.clear();
        return newUrlInvokerMap;
    }

客户端连接

在将url转换成invoker时RegistryDirectory#toInvokers中的DubboProtocol建立连接

invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);

DubboProtocol.refer
->AbstractProtocol#refer
->DubboProtocol#protocolBindingRefer
->DubboProtocol#getClients

DubboProtocol#getClients

获得客户端连接的方法

  • 判断是否为共享连接,默认是共享同一个连接进行通信
  • 是否配置了多个连接通道 connections,默认只有一个
   public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
        optimizeSerialization(url); //优化序列化
        // create rpc invoker. 构建DbbuoInvoker
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);
        return invoker;
    }
	// 获取ExchangeClient 服务通信连接
    private ExchangeClient[] getClients(URL url) {
        // whether to share connection
        boolean useShareConnect = false;
        int connections = url.getParameter(CONNECTIONS_KEY, 0);
        List<ReferenceCountExchangeClient> shareClients = null;
        // if not configured, connection is shared, otherwise, one connection for one service
        if (connections == 0) { //如果没有配置连接数,则默认为共享连接
            useShareConnect = true;
            String shareConnectionsStr = url.getParameter(SHARE_CONNECTIONS_KEY, (String) null);
            connections = Integer.parseInt(StringUtils.isBlank(shareConnectionsStr) ? ConfigUtils.getProperty(SHARE_CONNECTIONS_KEY,
                    DEFAULT_SHARE_CONNECTIONS) : shareConnectionsStr);
            shareClients = getSharedClient(url, connections);
        }
        ExchangeClient[] clients = new ExchangeClient[connections];
        for (int i = 0; i < clients.length; i++) {
            if (useShareConnect) {
                clients[i] = shareClients.get(i);

            } else {
                clients[i] = initClient(url);
            }
        }
        return clients;
    }

DubboProtocol#getSharedClient

获得一个共享连接

 private List<ReferenceCountExchangeClient> getSharedClient(URL url, int connectNum) {
        String key = url.getAddress();
        List<ReferenceCountExchangeClient> clients = referenceClientMap.get(key);
        //检查当前的key检查连接是否已经创建过并且可用,如果是,则直接返回并且增加连接的个数的统 计
        if (checkClientCanUse(clients)) {
            batchClientRefIncr(clients);
            return clients;
        }
        //如果连接已经关闭或者连接没有创建过
        locks.putIfAbsent(key, new Object());
        synchronized (locks.get(key)) {
            clients = referenceClientMap.get(key);
            // // 在创建连接之前,在做一次检查,防止连接并发创建
            if (checkClientCanUse(clients)) {
                batchClientRefIncr(clients);
                return clients;
            }
            // 连接数必须大于等于1
            connectNum = Math.max(connectNum, 1);
			//如果当前消费者还没有和服务端产生连接,则初始化
            if (CollectionUtils.isEmpty(clients)) {
                clients = buildReferenceCountExchangeClientList(url, connectNum);
                //创建clients之后,保存到map中
                referenceClientMap.put(key, clients);
            } else { //如果clients不为空,则从clients数组中进行遍历
                for (int i = 0; i < clients.size(); i++) {
                    ReferenceCountExchangeClient referenceCountExchangeClient = clients.get(i);
                  	// 如果在集合中存在一个连接但是这个连接处于closed状态,则重新构建一个 进行替换
                    if (referenceCountExchangeClient == null || referenceCountExchangeClient.isClosed()) {
                        clients.set(i, buildReferenceCountExchangeClient(url));
                        continue;
                    }
					//增加个数
                    referenceCountExchangeClient.incrementAndGetCount();
                }
            }
            /**
             * I understand that the purpose of the remove operation here is to avoid the expired url key
             * always occupying this memory space.
             */
            locks.remove(key);
            return clients;
        }
    }

DubboProtocol#buildReferenceCountExchangeClientList

  private List<ReferenceCountExchangeClient> buildReferenceCountExchangeClientList(URL url, int connectNum) {
        List<ReferenceCountExchangeClient> clients = new ArrayList<>();
        for (int i = 0; i < connectNum; i++) {
            clients.add(buildReferenceCountExchangeClient(url));
        }
        return clients;
    }

    private ReferenceCountExchangeClient buildReferenceCountExchangeClient(URL url) {
        ExchangeClient exchangeClient = initClient(url);
        return new ReferenceCountExchangeClient(exchangeClient);
    }

DubboProtocol#initClient

根据url构建连接

 private ExchangeClient initClient(URL url) {
        // 获得连接类型
        String str = url.getParameter(CLIENT_KEY, url.getParameter(SERVER_KEY, DEFAULT_REMOTING_CLIENT));
        // 序列化协议
        url = url.addParameter(CODEC_KEY, DubboCodec.NAME);
        // 心跳  
        url = url.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT));
        // BIO is not allowed since it has severe performance issue.
        // 判断str是否存在于扩展点中,如果不存在则直接报错
        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
            throw new RpcException("Unsupported client type: " + str + "," +
                    " supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
        }
        ExchangeClient client;
        try {
            // connection should be lazy
            // 是否需要延迟创建连接, 这里的requestHandler是一个适配器
            if (url.getParameter(LAZY_CONNECT_KEY, false)) {
                client = new LazyConnectExchangeClient(url, requestHandler);
            } else {
                client = Exchangers.connect(url, requestHandler);
            }
        } catch (RemotingException e) {
            throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
        }
        return client;
    }

Exchangers.connect

通过SPI创建一个客户端连接,默认HeaderExchanger

 public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }
        if (handler == null) {
            throw new IllegalArgumentException("handler == null");
        }
        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
        return getExchanger(url).connect(url, handler);
    }
 public static Exchanger getExchanger(URL url) {
      String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
      return getExchanger(type);
  }
  public static Exchanger getExchanger(String type) {
      return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
  }

HeaderExchanger.connect

@Override
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
   // NettyTransport.connect
   return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
 }

Transporters.connect

 public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
      if (url == null) {
          throw new IllegalArgumentException("url == null");
      }
      ChannelHandler handler;
      if (handlers == null || handlers.length == 0) {
          handler = new ChannelHandlerAdapter();
      } else if (handlers.length == 1) {
          handler = handlers[0];
      } else {
          handler = new ChannelHandlerDispatcher(handlers);
      }
      return getTransporter().connect(url, handler);
  }

   public static Transporter getTransporter() {
       return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
   }

NettyTransport.connect

构建一个netty的客户端连接,终于到这,不容易 ,后面是netty的知识可以参考netty章节的博客

@Override
public Client connect(URL url, ChannelHandler listener) throws RemotingException {
    return new NettyClient(url, listener);
}

NettyClient

    protected void doOpen() throws Throwable {
        final NettyClientHandler nettyClientHandler = new NettyClientHandler(getUrl(), this);
        bootstrap = new Bootstrap();
        bootstrap.group(nioEventLoopGroup)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                //.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getTimeout())
                .channel(NioSocketChannel.class);

        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.max(3000, getConnectTimeout()));
        bootstrap.handler(new ChannelInitializer() {

            @Override
            protected void initChannel(Channel ch) throws Exception {
                int heartbeatInterval = UrlUtils.getHeartbeat(getUrl());

                if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
                    ch.pipeline().addLast("negotiation", SslHandlerInitializer.sslClientHandler(getUrl(), nettyClientHandler));
                }

                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
                ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
                        .addLast("decoder", adapter.getDecoder())
                        .addLast("encoder", adapter.getEncoder())
                        .addLast("client-idle-handler", new IdleStateHandler(heartbeatInterval, 0, 0, MILLISECONDS))
                        .addLast("handler", nettyClientHandler);

                String socksProxyHost = ConfigUtils.getProperty(SOCKS_PROXY_HOST);
                if(socksProxyHost != null) {
                    int socksProxyPort = Integer.parseInt(ConfigUtils.getProperty(SOCKS_PROXY_PORT, DEFAULT_SOCKS_PROXY_PORT));
                    Socks5ProxyHandler socks5ProxyHandler = new Socks5ProxyHandler(new InetSocketAddress(socksProxyHost, socksProxyPort));
                    ch.pipeline().addFirst(socks5ProxyHandler);
                }
            }
        });
    }

    @Override
    protected void doConnect() throws Throwable {
        long start = System.currentTimeMillis();
        ChannelFuture future = bootstrap.connect(getConnectAddress());
        try {
            boolean ret = future.awaitUninterruptibly(getConnectTimeout(), MILLISECONDS);

            if (ret && future.isSuccess()) {
                Channel newChannel = future.channel();
                try {
                    // Close old channel
                    // copy reference
                    Channel oldChannel = NettyClient.this.channel;
                    if (oldChannel != null) {
                        try {
                            if (logger.isInfoEnabled()) {
                                logger.info("Close old netty channel " + oldChannel + " on create new netty channel " + newChannel);
                            }
                            oldChannel.close();
                        } finally {
                            NettyChannel.removeChannelIfDisconnected(oldChannel);
                        }
                    }
                } finally {
                    if (NettyClient.this.isClosed()) {
                        try {
                            if (logger.isInfoEnabled()) {
                                logger.info("Close new netty channel " + newChannel + ", because the client closed.");
                            }
                            newChannel.close();
                        } finally {
                            NettyClient.this.channel = null;
                            NettyChannel.removeChannelIfDisconnected(newChannel);
                        }
                    } else {
                        NettyClient.this.channel = newChannel;
                    }
                }
            } else if (future.cause() != null) {
                throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
                        + getRemoteAddress() + ", error message is:" + future.cause().getMessage(), future.cause());
            } else {
                throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
                        + getRemoteAddress() + " client-side timeout "
                        + getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start) + "ms) from netty client "
                        + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion());
            }
        } finally {
            // just add new valid channel to NettyChannel's cache
            if (!isConnected()) {
                //future.cancel(true);
            }
        }
    }

RegistryProtocol.refer 过程中有一个关键步骤,即在监听到服务提供者url时触发RegistryDirectory.notify() 方法。
RegistryDirectory.notify() 方法调用 refreshInvoker() 方法将服务提供者urls转换为对应的 远 程invoker ,最终调用到 DubboProtocol.refer()方法生成对应的 DubboInvoker 。 DubboInvoker 的构造方法中有一项入参 ExchangeClient[] clients ,即对应本文要讲的网络客户
端 Client 。DubboInvoker就是通过调用 client.request() 方法完成网络通信的请求发送和响应接收功能。Client 的具体生成过程就是通过 DubboProtocol 的 initClient(URL url) 方法创建了一个HeaderExchangeClient 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值