Dubbo服务引用(二)远程引用

上篇本地,这篇远程。
createProxy(map) 方法中,涉及远程引用服务:

/**
 * 服务引用 URL 数组
 */
private final List<URL> urls = new ArrayList<URL>();
/**
 * 直连服务地址
 *
 * 1. 可以是注册中心,也可以是服务提供者
 * 2. 可配置多个,使用 ; 分隔
 */
// url for peer-to-peer invocation
private String url;

  /**
   * 创建 Service 代理对象
   *
   * @param map 集合
   * @return 代理对象
   */
  @SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
  private T createProxy(Map<String, String> map) {
      URL tmpUrl = new URL("temp", "localhost", 0, map);
      // 【省略代码】是否本地引用
      final boolean isJvmRefer;
  
      // 【省略代码】本地引用
      if (isJvmRefer) {
     // 正常流程,一般为远程引用
      } else {
          // 定义直连地址,可以是服务提供者的地址,也可以是注册中心的地址
          if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
              // 拆分地址成数组,使用 ";" 分隔。
              String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
              // 循环数组,添加到 `url` 中。
              if (us != null && us.length > 0) {
                  for (String u : us) {
                      // 创建 URL 对象
                      URL url = URL.valueOf(u);
                      // 设置默认路径
                      if (url.getPath() == null || url.getPath().length() == 0) {
                          url = url.setPath(interfaceName);
                      }
                      // 注册中心的地址,带上服务引用的配置参数
                      if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                          urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                      // 服务提供者的地址
                      } else {
                          urls.add(ClusterUtils.mergeUrl(url, map));
                      }
                  }
              }
          // 注册中心
          } else { // assemble URL from register center's configuration
              // 加载注册中心 URL 数组
              List<URL> us = loadRegistries(false);
              // 循环数组,添加到 `url` 中。
              if (us != null && !us.isEmpty()) {
                  for (URL u : us) {
                      // 加载监控中心 URL
                      URL monitorUrl = loadMonitor(u);
                      // 服务引用配置对象 `map`,带上监控中心的 URL
                      if (monitorUrl != null) {
                          map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                      }
                      // 注册中心的地址,带上服务引用的配置参数
                      urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map))); // 注册中心,带上服务引用的配置参数
                  }
              }
              if (urls == null || urls.isEmpty()) {
                  throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
              }
          }
  
          // 单 `urls` 时,引用服务,返回 Invoker 对象
          if (urls.size() == 1) {
              // 引用服务
              invoker = refprotocol.refer(interfaceClass, urls.get(0));
          } else {
              // 循环 `urls` ,引用服务,返回 Invoker 对象
              List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
              URL registryURL = null;
              for (URL url : urls) {
                  // 引用服务
                  invokers.add(refprotocol.refer(interfaceClass, url));
                  // 使用最后一个注册中心的 URL
                  if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                      registryURL = url; // use last registry url
                  }
              }
              // 有注册中心
              if (registryURL != null) { // registry url is available
                  // 对有注册中心的 Cluster 只用 AvailableCluster
                  // use AvailableCluster only when register's cluster is available
                  URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
                  invoker = cluster.join(new StaticDirectory(u, invokers));
              // 无注册中心
              } else { // not a registry url
                  invoker = cluster.join(new StaticDirectory(invokers));
              }
          }
      }
  
      // 【省略代码】启动时检查
  
     // 创建 Service 代理对象
      // create service proxy
      return (T) proxyFactory.getProxy(invoker);
  }

mdzz这看来看去跟最早一篇远程暴露极像啊,
ProtocolFilterWrapper:refer(type, url) 方法:

 public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
     // 注册中心:当 invoker.url.protocl = registry ,注册中心的 URL ,无需创建 Filter 过滤链
     if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
         return protocol.refer(type, url);
     }
     // 引用服务,返回 Invoker 对象
     // 给改 Invoker 对象,包装成带有 Filter 过滤链的 Invoker 对象
     return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
 }

RegistryProtocol:refer(type, url) 方法:

/**
 * Cluster 自适应拓展实现类对象
 */
private Cluster cluster;

  public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
      // 获得真实的注册中心的 URL
      url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
      // 获得注册中心
      Registry registry = registryFactory.getRegistry(url);
      // TODO 芋艿
      if (RegistryService.class.equals(type)) {
          return proxyFactory.getInvoker((T) registry, type, url);
      }
  
      // 获得服务引用配置参数集合
      // group="a,b" or group="*"
      Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
      String group = qs.get(Constants.GROUP_KEY);
      // 分组聚合,参见文档 http://dubbo.apache.org/zh-cn/docs/user/demos/group-merger.html
      if (group != null && group.length() > 0) {
          if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                  || "*".equals(group)) {
              // 执行服务引用
              return doRefer(getMergeableCluster(), registry, type, url);
          }
      }
      // 执行服务引用
      return doRefer(cluster, registry, type, url);
  }

getMergeableCluster() 方法,获得可合并的 Cluster 对象:

private Cluster getMergeableCluster() {
    return ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("mergeable");
}

doRefer(cluster, registry, type, url) 方法,执行服务引用的逻辑:

 /**
  * 执行服务引用,返回 Invoker 对象
  *
  * @param cluster Cluster 对象
  * @param registry 注册中心对象
  * @param type 服务接口类型
  * @param url 注册中心 URL
  * @param <T> 泛型
  * @return Invoker 对象
  */
 private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
     // 创建 RegistryDirectory 对象,并设置注册中心
     RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
     directory.setRegistry(registry);
     directory.setProtocol(protocol);
     // 创建订阅 URL
     // all attributes of REFER_KEY
     Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters()); // 服务引用配置集合
     URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
    // 向注册中心注册自己(服务消费者)
     if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
             && url.getParameter(Constants.REGISTER_KEY, true)) {
         registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, constants.CONSUMERS_CATEGORY,
                 Constants.CHECK_KEY, String.valueOf(false)));
     }
     // 向注册中心订阅服务提供者
     directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
             Constants.PROVIDERS_CATEGORY
                     + "," + Constants.CONFIGURATORS_CATEGORY
                     + "," + Constants.ROUTERS_CATEGORY));
 
     // 创建 Invoker 对象
     Invoker invoker = cluster.join(directory);
     // 向本地注册表,注册消费者
     ProviderConsumerRegTable.registerConsuemr(invoker, url, subscribeUrl, directory);
     return invoker;
 }

DubboProtocol:refer(type, url) 方法:

// AbstractProtocol.java 父类
/**
 * Invoker 集合
 */
//TODO SOFEREFENCE
protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();

// DubboProtocol.java

  public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
      // 初始化序列化优化器
      optimizeSerialization(url);
      // 获得远程通信客户端数组
      // 创建 DubboInvoker 对象
      // create rpc invoker.
      DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
      // 添加到 `invokers`
      invokers.add(invoker);
      return invoker;
  }

getClients(url) 方法,获得连接服务提供者的远程通信客户端数组:

 /**
  * 获得连接服务提供者的远程通信客户端数组
  *
  * @param url 服务提供者 URL
  * @return 远程通信客户端
  */
 private ExchangeClient[] getClients(URL url) {
     // 是否共享连接
     // whether to share connection
     boolean service_share_connect = false;
     int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
     // if not configured, connection is shared, otherwise, one connection for one service
     if (connections == 0) { // 未配置时,默认共享
         service_share_connect = true;
         connections = 1;
     }
 
     // 创建连接服务提供者的 ExchangeClient 对象数组
     ExchangeClient[] clients = new ExchangeClient[connections];
     for (int i = 0; i < clients.length; i++) {
         if (service_share_connect) { // 共享
             clients[i] = getSharedClient(url);
         } else { // 不共享
             clients[i] = initClient(url);
         }
     }
     return clients;
 }

#getClients(url) 方法,获得连接服务提供者的远程通信客户端数组:

/**
 * 通信客户端集合
 *
 * key: 服务器地址。格式为:host:port
 */
private final Map<String, ReferenceCountExchangeClient> referenceClientMap = new ConcurrentHashMap<String, ReferenceCountExchangeClient>(); // <host:port,Exchanger>
/**
 * TODO 8030 ,这个是什么用途啊。
 *
 * key: 服务器地址。格式为:host:port 。和 {@link #referenceClientMap} Key ,是一致的。
 */
private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap = new ConcurrentHashMap<String, LazyConnectExchangeClient>();

  private ExchangeClient getSharedClient(URL url) {
      // 从集合中,查找 ReferenceCountExchangeClient 对象
      String key = url.getAddress();
      ReferenceCountExchangeClient client = referenceClientMap.get(key);
      if (client != null) {
          // 若未关闭,增加指向该 Client 的数量,并返回它
          if (!client.isClosed()) {
              client.incrementAndGetCount();
              return client;
          // 若已关闭,移除
          } else {
              referenceClientMap.remove(key);
          }
      }
      // 同步,创建 ExchangeClient 对象。
      synchronized (key.intern()) {
          // 创建 ExchangeClient 对象
          ExchangeClient exchangeClient = initClient(url);
          // 将 `exchangeClient` 包装,创建 ReferenceCountExchangeClient 对象
          client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
          // 添加到集合
          referenceClientMap.put(key, client);
          // 添加到 `ghostClientMap`
          ghostClientMap.remove(key);
          return client;
      }
  }

ghostClientMap,幽灵客户端集合:

添加:每次 ReferenceCountExchangeClient 彻底关闭( 指向归零 ) ,其内部的 client 会替换成重新创建的 LazyConnectExchangeClient 对象,此时叫这个对象为幽灵客户端,添加到 ghostClientMap 中。
移除:当幽灵客户端,对应的 URL 的服务器被重新连接上后,会被移除。
注意,在幽灵客户端被移除之前,referenceClientMap 中,依然保留着对应的 URL 的 ReferenceCountExchangeClient 对象。所以,ghostClientMap 相当于标记 referenceClientMap 中,哪些 LazyConnectExchangeClient 对象,是幽灵状态。

initClient(url) 方法,创建 ExchangeClient 对象,”连接”服务器:

 private ExchangeClient initClient(URL url) {
     // 校验 Client 的 Dubbo SPI 拓展是否存在
     // client type setting.
     String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
     // BIO is not allowed since it has severe performance issue.
     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(), " "));
     }
 
     // 设置编解码器为 Dubbo ,即 DubboCountCodec
     url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
 
     // 默认开启 heartbeat
     // enable heartbeat by default
     url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
 
     // 连接服务器,创建客户端
     ExchangeClient client;
     try {
         // 懒连接,创建 LazyConnectExchangeClient 对象
         // connection should be lazy
         if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
             client = new LazyConnectExchangeClient(url, requestHandler);
         // 直接连接,创建 HeaderExchangeClient 对象
         } 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;
 }

DubboInvoker实现 AbstractExporter 抽象类,Dubbo Invoker 实现类:

 /**
  * 远程通信客户端数组
  */
 private final ExchangeClient[] clients;
 /**
  * 使用的 {@link #clients} 的位置
  */
 private final AtomicPositiveInteger index = new AtomicPositiveInteger();
 /**
  * 版本
  */
 private final String version;
 /**
  * 销毁锁
  *
  * 在 {@link #destroy()} 中使用
  */
 private final ReentrantLock destroyLock = new ReentrantLock();
 /**
  * Invoker 集合,从 {@link DubboProtocol#invokers} 获取
  */
 private final Set<Invoker<?>> invokers;
 
 public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients) {
     this(serviceType, url, clients, null);
 }
 
 public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers) {
 //调用父类构造方法。该方法中,会将 interface group version token timeout 添加到公用的隐式传参 AbstractInvoker.attachment 属性。
     super(serviceType, url, new String[]{Constants.INTERFACE_KEY, Constants.GROUP_KEY, Constants.TOKEN_KEY, Constants.TIMEOUT_KEY});
     this.clients = clients;
     // get version.
     this.version = url.getParameter(Constants.VERSION_KEY, "0.0.0");
     this.invokers = invokers;
 }

ReferenceCountExchangeClient实现 ExchangeClient 接口,支持指向计数的信息交换客户端实现类。
构造方法:

 /**
  * URL
  */
 private final URL url;
 /**
  * 指向数量
  */
 private final AtomicInteger refenceCount = new AtomicInteger(0);
 /**
  * 幽灵客户端集合
  */
 private final ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap;
 /**
  * 客户端
  */
 private ExchangeClient client;
 
 public ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap) {
     this.client = client;
     // 指向加一
     refenceCount.incrementAndGet();
     this.url = client.getUrl();
     if (ghostClientMap == null) {
         throw new IllegalStateException("ghostClientMap can not be null, url: " + url);
     }
     this.ghostClientMap = ghostClientMap;
 }

装饰器模式,每个实现方法都是调用client的对应方法:

@Override
public void send(Object message) throws RemotingException {
    client.send(message);
}

计数:

public void incrementAndGetCount() {
    refenceCount.incrementAndGet();
}

关闭:

 @Override
 public void close(int timeout) {
     if (refenceCount.decrementAndGet() <= 0) {
         // 关闭 `client`
         if (timeout == 0) {
             client.close();
         } else {
             client.close(timeout);
         }
         // 替换 `client` 为 LazyConnectExchangeClient 对象。
         client = replaceWithLazyClient();
     }
 }

上面调用replaceWithLazyClient() 方法,替换 client 为 LazyConnectExchangeClient 对象:

 private LazyConnectExchangeClient replaceWithLazyClient() {
     // this is a defensive operation to avoid client is closed by accident, the initial state of the client is false
     URL lazyUrl = url.addParameter(Constants.LAZY_CONNECT_INITIAL_STATE_KEY, Boolean.FALSE)
             .addParameter(Constants.RECONNECT_KEY, Boolean.FALSE) // 不重连
             .addParameter(Constants.SEND_RECONNECT_KEY, Boolean.TRUE.toString())
             .addParameter("warning", Boolean.TRUE.toString())
             .addParameter(LazyConnectExchangeClient.REQUEST_WITH_WARNING_KEY, true)
             .addParameter("_client_memo", "referencecounthandler.replacewithlazyclient"); // 备注
 
     // 创建 LazyConnectExchangeClient 对象,若不存在。
     String key = url.getAddress();
     // in worst case there's only one ghost connection.
     LazyConnectExchangeClient gclient = ghostClientMap.get(key);
     if (gclient == null || gclient.isClosed()) {
         gclient = new LazyConnectExchangeClient(lazyUrl, client.getExchangeHandler());
         ghostClientMap.put(key, gclient);
     }
     return gclient;
 }

LazyConnectExchangeClient实现 ExchangeClient 接口,支持懒连接服务器的信息交换客户端实现类。
构造:

 static final String REQUEST_WITH_WARNING_KEY = "lazyclient_request_with_warning";
 
 /**
  * URL
  */
 private final URL url;
 /**
  * 通道处理器
  */
 private final ExchangeHandler requestHandler;
 /**
  * 连接锁
  */
 private final Lock connectLock = new ReentrantLock();
 /**
  * lazy connect 如果没有初始化时的连接状态
  */
 // lazy connect, initial state for connection
 private final boolean initialState;
 /**
  * 通信客户端
  */
 private volatile ExchangeClient client;
 /**
  * 请求时,是否检查告警
  */
 protected final boolean requestWithWarning;
 /**
  * 警告计数器。每超过一定次数,打印告警日志。参见 {@link #warning(Object)}
  */
 private AtomicLong warningcount = new AtomicLong(0);
 
 public LazyConnectExchangeClient(URL url, ExchangeHandler requestHandler) {
     // lazy connect, need set send.reconnect = true, to avoid channel bad status.
     this.url = url.addParameter(Constants.SEND_RECONNECT_KEY, Boolean.TRUE.toString());
     this.requestHandler = requestHandler;
     this.initialState = url.getParameter(Constants.LAZY_CONNECT_INITIAL_STATE_KEY, Constants.DEFAULT_LAZY_CONNECT_INITIAL_STATE);
     this.requestWithWarning = url.getParameter(REQUEST_WITH_WARNING_KEY, false);
 }

isConnected() 方法:

@Override
public boolean isConnected() {
    if (client == null) { // 客户端未初始化
        return initialState;
    } else {
        return client.isConnected();
    }
}

warningcount 属性,警告计数器。每超过一定次数,打印告警日志。每次发送请求时,会调用 warning(request) 方法,根据情况,打印告警日志:

private void warning(Object request) {
    if (requestWithWarning) { // 开启
        if (warningcount.get() % 5000 == 0) { // 5000 次
            logger.warn(new IllegalStateException("safe guard client , should not be called ,must have a bug."));
        }
        warningcount.incrementAndGet(); // 增加计数
    }
}

初始化客户端:

private void initClient() throws RemotingException {
    // 已初始化,跳过
    if (client != null) {
        return;
    }
    if (logger.isInfoEnabled()) {
        logger.info("Lazy connect to " + url);
    }
    // 获得锁
    connectLock.lock();
    try {
        // 已初始化,跳过
        if (client != null) {
            return;
        }
        // 创建 Client ,连接服务器
        this.client = Exchangers.connect(url, requestHandler);
    } finally {
        // 释放锁
        connectLock.unlock();
    }
}

发送消息/请求前,都会调用该方法,保证客户端已经初始化:

public void send(Object message, boolean sent) throws RemotingException {
    initClient();
    client.send(message, sent);
}

@Override
public ResponseFuture request(Object request, int timeout) throws RemotingException {
    warning(request);
    initClient();
    return client.request(request, timeout);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值