Dubbo服务暴露(二)之远程暴露

相比本地暴露,远程暴露会多做如下几件事情:

  1. 启动通信服务器,绑定服务端口,提供远程调用。
  2. 向注册中心注册服务提供者,提供服务消费者从注册中心发现服务。

上次分享本地暴露,该方式仅使用 Injvm 协议实现,具体代码在 dubbo-rpc-injvm 模块中。
这篇分享远程暴露,该方式有多种协议实现,例如 Dubbo ( 默认协议 )、Hessian 、Rest 等等。

doExportUrlsFor1Protocol(protocolConfig, registryURLs) 方法中,涉及远程暴露服务的代码如下:

 // 服务远程暴露
 // export to remote if the config is not local (export to local only when config is local)
 if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
     
     if (logger.isInfoEnabled()) {
     
         logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
     }
     if (registryURLs != null && !registryURLs.isEmpty()) {
     
         for (URL registryURL : registryURLs) {
     
             // "dynamic" :服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。
             url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
             // 获得监控中心 URL
             URL monitorUrl = loadMonitor(registryURL); // TODO 芋艿,监控
             if (monitorUrl != null) {
     
                 url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
             }
             if (logger.isInfoEnabled()) {
     
                 logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
             }
            // 使用 ProxyFactory 创建 Invoker 对象
             Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
 
             // 创建 DelegateProviderMetaDataInvoker 对象
             DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
 
             // 使用 Protocol 暴露 Invoker 对象
             Exporter<?> exporter = protocol.export(wrapperInvoker);
             // 添加到 `exporters`
             exporters.add(exporter);
         }
     } else {
      // 用于被服务消费者直连服务提供者,参见文档 http://dubbo.apache.org/zh-cn/docs/user/demos/explicit-target.html 。主要用于开发测试环境使用。
         // 使用 ProxyFactory 创建 Invoker 对象
         Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
 
         // 创建 DelegateProviderMetaDataInvoker 对象
         DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
 
         // 使用 Protocol 暴露 Invoker 对象
         Exporter<?> exporter = protocol.export(wrapperInvoker);
         // 添加到 `exporters`
         exporters.add(exporter);
     }
 }
 /*
 Protocol 有两个 Wrapper 拓展实现类: ProtocolFilterWrapper、ProtocolListenerWrapper 。所以,export(...) 方法的调用顺序是:

Protocol$Adaptive => ProtocolFilterWrapper => ProtocolListenerWrapper => RegistryProtocol
=>
Protocol$Adaptive => ProtocolFilterWrapper => ProtocolListenerWrapper => DubboProtocol
也就是说,这一条大的调用链,包含两条小的调用链。原因是:
首先,传入的是注册中心的 URL ,通过 Protocol$Adaptive 获取到的是 RegistryProtocol 对象。
其次,RegistryProtocol 会在其 export(...) 方法中,使用服务提供者的 URL ( 即注册中心的 URL 的 export 参数值),再次调用 Protocol$Adaptive 获取到的是 DubboProtocol 对象,进行服务暴露。*/

上面调用 export(invoker) 方法,暴露服务。这里体现出Dubbo SPI自适应特性的好处:可以自动根据URL参数,获得对应的拓展实现 (invoker 传入后,根据 invoker.url 自动获得对应 Protocol 拓展实现为 DubboProtocol )。

loadMonitor(registryURL) 方法,加载监控中心 com.alibaba.dubbo.common.URL 数组:

 /**
  * 加载监控中心 URL
  *
  * @param registryURL 注册中心 URL
  * @return 监控中心 URL
  */
 protected URL loadMonitor(URL registryURL) {
     
     // 从 属性配置 中加载配置到 MonitorConfig 对象。
     if (monitor == null) {
     
         String monitorAddress = ConfigUtils.getProperty("dubbo.monitor.address");
         String monitorProtocol = ConfigUtils.getProperty("dubbo.monitor.protocol");
         if ((monitorAddress == null || monitorAddress.length() == 0) && (monitorProtocol == null || monitorProtocol.length() == 0)) {
     
             return null;
         }
 
         monitor = new MonitorConfig();
         if (monitorAddress != null && monitorAddress.length() > 0) {
     
             monitor.setAddress(monitorAddress);
         }
         if (monitorProtocol != null && monitorProtocol.length() > 0) {
     
             monitor.setProtocol(monitorProtocol);
         }
     }
     appendProperties(monitor);
     // 添加 `interface` `dubbo` `timestamp` `pid` 到 `map` 集合中
     Map<String, String> map = new HashMap<String, String>();
     map.put(Constants.INTERFACE_KEY, MonitorService.class.getName());
     map.put("dubbo", Version.getVersion());
     map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
     if (ConfigUtils.getPid() > 0) {
     
         map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
     }
     // 将 MonitorConfig ,添加到 `map` 集合中。
     appendParameters(map, monitor);
     // 获得地址
     String address = monitor.getAddress();
     String sysaddress = System.getProperty("dubbo.monitor.address");
     if (sysaddress != null && sysaddress.length() > 0) {
     
         address = sysaddress;
     }
     // 直连监控中心服务器地址
     if (ConfigUtils.isNotEmpty(address)) {
     
         // 若不存在 `protocol` 参数,默认 "dubbo" 添加到 `map` 集合中。
         if (!map.containsKey(Constants.PROTOCOL_KEY)) {
     
             if (ExtensionLoader.getExtensionLoader(MonitorFactory.class).hasExtension("logstat")) {
     
                 map.put(Constants.PROTOCOL_KEY, "logstat");
             } else {
     
                 map.put(Constants.PROTOCOL_KEY, "dubbo");
             }
         }
         // 解析地址,创建 Dubbo URL 对象。
         return UrlUtils.parseURL(address, map);
     // 从注册中心发现监控中心地址
     } else if (Constants.REGISTRY_PROTOCOL.equals(monitor.getProtocol()) && registryURL != null) {
     
         return registryURL.setProtocol("dubbo").addParameter(Constants.PROTOCOL_KEY, "registry").addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map));
     }
     return null;
 }

export(invoker) 方法:

 public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
     
     // 注册中心
     if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
     
         return protocol.export(invoker);
     }
     // 建立带有 Filter 过滤链的 Invoker ,再暴露服务。
    return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
 }

RegistryProtocol ,实现 Protocol 接口,注册中心协议实现类:
属性:


/**
 * 单例。在 Dubbo SPI 中,被初始化,有且仅有一次。
 */
private static RegistryProtocol INSTANCE;

/**
 * 绑定关系集合。
 *
 * key:服务 Dubbo URL
 */
// To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed.
// 用于解决rmi重复暴露端口冲突的问题,已经暴露过的服务不再重新暴露
// providerurl <--> exporter
private final Map<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<String, ExporterChangeableWrapper<?>>();
/**
 * Protocol 自适应拓展实现类,通过 Dubbo SPI 自动注入。
 */
private Protocol protocol;
/**
 * RegistryFactory 自适应拓展实现类,通过 Dubbo SPI 自动注入。用于创建注册中心 Registry 对象。
 */
private RegistryFactory registryFactory;

public RegistryProtocol() {
     
    INSTANCE = this;
}

public static RegistryProtocol getRegistryProtocol() {
     
    if (INSTANCE == null) {
     
        ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(Constants.REGISTRY_PROTOCOL); // load
    }
    return INSTANCE;
}

export(invoker) 方法:

 public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
     
     // 暴露服务
     // export invoker
     final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
 
     // 获得注册中心 URL
     URL registryUrl = getRegistryUrl(originInvoker);
 
     // 获得注册中心对象
     // registry provider
     final Registry registry = getRegistry(originInvoker);
 
     // 获得服务提供者 URL
     final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
 
     //to judge to delay publish whether or not
     boolean register = registedProviderUrl.getParameter("register", true);
 
     // 向本地注册表,注册服务提供者
     ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl);
 
     // 向注册中心注册服务提供者(自己)
     if (register) {
     
         register(registryUrl, registedProviderUrl);
         ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true); // 标记向本地注册表的注册服务提供者,已经注册
     } 
 
     // 使用 OverrideListener 对象,订阅配置规则
     // Subscribe the override data
     // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
     final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
     final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
     overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
     registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
     //Ensure that a new exporter instance is returned every time export
     return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registedProviderUrl);
 }

getRegistryUrl(originInvoker) 方法,获得注册中心 URL :

/**
 * 获得注册中心 URL
 *
 * @param originInvoker 原始 Invoker
 * @return URL
 */
private URL getRegistryUrl(Invoker<?> originInvoker) {
     
    URL registryUrl = originInvoker.getUrl();
    if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
      // protocol
        String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
        registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
    }
    return registryUrl;
}

getRegistedProviderUrl(originInvoker) 方法,获得服务提供者 URL :

private URL getRegistedProviderUrl(final Invoker<?> originInvoker) {
     
    // 从注册中心的 export 参数中,获得服务提供者的 URL
    URL providerUrl = getProviderUrl(originInvoker);
    //The address you see at the registry。移除多余的参数。因为,这些参数注册到注册中心没有实际的用途
    return providerUrl.removeParameters(getFilteredKeys(providerUrl)) // 移除 .hide 为前缀的参数
            .removeParameter(Constants.MONITOR_KEY) // monitor
            .removeParameter(Constants.BIND_IP_KEY) // bind.ip
            .removeParameter(Constants.BIND_PORT_KEY) // bind.port
            .removeParameter(QOS_ENABLE) // qos.enable
            .removeParameter(QOS_PORT) // qos.port
            .removeParameter(ACCEPT_FOREIGN_IP); // qos.accept.foreign.ip
}

private URL getProviderUrl(final Invoker<?> origininvoker) {
     
    String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY); // export
    if (export == null || export.length() == 0) {
     
        throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl());
    }
    return URL.valueOf(export);
}

private static String[] getFilteredKeys(URL url) {
     
    Map<String, String> params = url.getParameters();
    if (params != null && !params.isEmpty()) {
     
        List<String> filteredKeys = new ArrayList<String>();
        for (Map.Entry<String, String> entry : params.entrySet()) {
     
            if (entry != null && entry.getKey() != null && entry.getKey().startsWith(Constants.HIDE_KEY_PREFIX)) {
     
                filteredKeys.add(entry.getKey());
            }
        }
        return filteredKeys.toArray(new String[filteredKeys.size()]);
    } else {
     
        return new String[]{
     };
    }
}

register(registryUrl, registedProviderUrl) 方法,向注册中心注册服务提供者(自己:

public void register(URL registryUrl, URL registedProviderUrl) {
     
    Registry registry = registryFactory.getRegistry(registryUrl);
    registry.register(registedProviderUrl);
}

doLocalExport() 方法,暴露服务。此处的 Local 指的是,本地启动服务,但是不包括向注册中心注册服务的意思:

 /**
  * 暴露服务。
  *
  * 此处的 Local 指的是,本地启动服务,但是不包括向注册中心注册服务的意思。
  *
  * @param originInvoker 原始 Invoker
  * @param  泛型
  * @return Exporter 对象
  */
 @SuppressWarnings("unchecked")
 private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
     
     // 获得在 `bounds` 中的缓存 Key
     String key = getCacheKey(originInvoker);
     // 从 `bounds` 获得,是不是已经暴露过服务
     ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
     if (exporter == null) {
     
         synchronized (bounds) {
     
             exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
             // 未暴露过,进行暴露服务
             if (exporter == null) {
     
                 // 创建 Invoker Delegate 对象
                 final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
                 // 暴露服务,创建 Exporter 对象
                 // 使用 创建的Exporter对象 + originInvoker ,创建 ExporterChangeableWrapper 对象
                 exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
                 // 添加到 `bounds`
                 bounds.put(key, exporter);
             }
         }
     }
     return exporter;
 }

getCacheKey(originInvoker) 方法,获得在 bounds 中的缓存 Key :

/**
 * Get the key cached in bounds by invoker
 *
 * 获 取invoker 在 bounds中 缓存的key
 *
 * @param originInvoker 原始 Invoker
 * @return url 字符串
 */
private String getCacheKey(final Invoker<?> originInvoker) {
     
    URL providerUrl = getProviderUrl(originInvoker);
    return providerUrl.removeParameters("dynamic", "enabled").toFullString();
}

DubboProtocol ,实现 AbstractProtocol 抽象类,Dubbo 协议实现类:
属性:

// ... 省略部分和本文无关的属性。

/**
 * 通信服务器集合
 *
 * key: 服务器地址。格式为:host:port
 */
private final Map<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>(); // 

export(invoker) 方法:

 public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
     
     URL url = invoker.getUrl();
 
     // 创建 DubboExporter 对象,并添加到 `exporterMap` 。
     // export service.
     String key = serviceKey(url);
     DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
     exporterMap.put(key, exporter);
 
     // TODO 【8033 参数回调】
     //export an stub service for dispatching event
     Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
     Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
     if (isStubSupportEvent && !isCallbackservice) {
     
         String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
         if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
     
             if (logger.isWarnEnabled()) {
     
                logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
                         "], has set stubproxy support event ,but no stub methods founded."));
             }
         } else {
     
             stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
         }
     }
 
     // 启动服务器
     openServer(url);
 
     // 初始化序列化优化器
     optimizeSerialization(url);
     return exporter;
 }

openServer(url) 方法,启动通信服务器:

/**
 * 通信客户端集合
 *
 * key: 服务器地址。格式为:host:port
 */
private final Map<String, ReferenceCountExchangeClient> referenceClientMap = new ConcurrentHashMap<String, ReferenceCountExchangeClient>(); // 

  /**
   * 启动服务器
   *
   * @param url URL
   */
  private void openServer(URL url) {
     
      // find server.获得服务器地址。
      String key = url.getAddress();
      //client can export a service which's only for server to invoke  
      // 配置项 isserver ,可以暴露一个仅当前 JVM 可调用的服务。目前该配置项已经不存在。
      boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true); // isserver
      if (isServer) {
     
      // 从 serverMap 获得对应服务器地址已存在的通信服务器。即,不重复创建
          ExchangeServer server = serverMap.get(key);
          if (server == null) {
     
          // 通信服务器不存在,调用 createServer(url) 方法,创建服务器。
              serverMap.put(key, createServer(url));
          } else {
     
              // server supports reset, use together with override18 行:通信服务器已存在,调用 reset(url) 方法,重置服务器的属性
              server.reset(url);
          }
      }
  }

createServer(url) 方法,创建并启动通信服务器:

 private ExchangeServer createServer(URL url) {
     
     // 默认开启 server 关闭时发送 READ_ONLY 事件
     // send readonly event when server closes, it's enabled by default
     url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
     // 默认开启 heartbeat
     // enable heartbeat by default
     url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
 
     // 校验 Server 的 Dubbo SPI 拓展是否存在
     String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
     if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
     
         throw new RpcException("Unsupported server type: " + str + ", url: " + url);
    }
 
     // 设置编解码器为 `"Dubbo"` 
     url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
 
     // 启动服务器
     ExchangeServer server;
     try {
     
         server = Exchangers.bind(url, requestHandler);
     } catch (RemotingException e) {
     
         throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
     }
 
     // 校验 Client 的 Dubbo SPI 拓展是否存在
     str = url.getParameter(Constants.CLIENT_KEY);
     if (str != null && str.length() > 0) {
     
         Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
         if (!supportedTypes.contains(str)) {
     
             throw new RpcException("Unsupported client type: " + str);
         }
     }
     return server;
 }

ExporterChangeableWrapper ,实现 Exporter 接口,Exporter 可变的包装器:

private class ExporterChangeableWrapper<T> implements Exporter<T> {
     

    /**
     * 原 Invoker 对象
     */
    private final Invoker<T> originInvoker;
    /**
     * 暴露的 Exporter 对象
     */
    private Exporter<T> exporter;

    public ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker) {
     
        this.exporter = exporter;
        this.originInvoker = originInvoker;
    }

    public Invoker<T> getOriginInvoker() {
     
        return originInvoker;
    }

    public Invoker<T> getInvoker() {
     
        return exporter.getInvoker();
    }

    public void setExporter(Exporter<T> exporter) {
     
        this.exporter = exporter;
    }

    public void unexport() {
     
        String key = getCacheKey(this.originInvoker);
        // 移除出 `bounds`
        bounds.remove(key);
        // 取消暴露
        exporter.unexport();
    }
}

DestroyableExporter ,实现 Exporter 接口,可销毁的 Exporter 实现类:

private class ExporterChangeableWrapper<T> implements Exporter<T> {
     

    /**
     * 原 Invoker 对象
     */
    private final Invoker<T> originInvoker;
    /**
     * 暴露的 Exporter 对象
     */
    private Exporter<T> exporter;

    public ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker) {
     
        this.exporter = exporter;
        this.originInvoker = originInvoker;
    }

    public Invoker<T> getOriginInvoker() {
     
        return originInvoker;
    }

    @Override
    public Invoker<T> getInvoker() {
     
        return exporter.getInvoker();
    }

    // 可以重新设置 Exporter 对象
    public void setExporter(Exporter<T> exporter) {
     
        this.exporter = exporter;
    }

    @Override
    public void unexport() {
     
        String key = getCacheKey(this.originInvoker);
        // 移除出 `bounds`
        bounds.remove(key);
        // 取消暴露
        exporter.unexport();
    }

}

DubboExporter ,实现 AbstractExporter 抽象类,Dubbo Exporter 实现类:

public class DubboExporter<T> extends AbstractExporter<T> {
     

    /**
     * 服务键
     */
    private final String key;
    /**
     * Exporter 集合
     *
     * key: 服务键
     *
     * 该值实际就是 {@link com.alibaba.dubbo.rpc.protocol.AbstractProtocol#exporterMap}
     */
    private final Map<String, Exporter<?>> exporterMap;

    public DubboExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
     
        super(invoker);
        this.key = key;
        this.exporterMap = exporterMap;
    }

    @Override
    public void unexport() {
     
        // 取消暴露
        super.unexport();
  // 将自己移除出 exporterMap 中。
        exporterMap.remove(key);
    }

}

你可能感兴趣的:(dubbo)