精尽 Dubbo 源码分析 —— 服务暴露

1. 概述

Dubbo 服务暴露有两种方式

  • 本地暴露,JVM 本地调用。配置如下:

  • 远程暴露,网络远程通信。配置如下:

在不配置 scope 的情况下,默认两种方式都暴露

2.本地暴露

2.1 doExportUrls

本地暴露服务的顺序图如下:

ServiceConfig#export() 方法中,会在配置初始化完成后,调用顺序图的起点 #doExportUrls() 方法,开始暴露服务。代码如下:

     /**
     * 暴露 Dubbo URL
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private void doExportUrls() {
        // 加载注册中心 URL 数组
        List registryURLs = loadRegistries(true);
        // 循环 `protocols` ,向逐个注册中心分组暴露服务。
        for (ProtocolConfig protocolConfig : protocols) {
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }
    

2.2 loadRegistries

loadRegistries(provider) 方法,加载注册中心 com.alibaba.dubbo.common.URL 数组

/**
     * 加载注册中心 URL 数组
     *
     * @param provider 是否是服务提供者
     * @return URL 数组
     */
    protected List loadRegistries(boolean provider) {
        // 校验 RegistryConfig 配置数组。
        checkRegistry();
        // 创建 注册中心 URL 数组
        List registryList = new ArrayList();
        if (registries != null && !registries.isEmpty()) {
            for (RegistryConfig config : registries) {
                // 获得注册中心的地址
                String address = config.getAddress();
                if (address == null || address.length() == 0) {
                    address = Constants.ANYHOST_VALUE;
                }
                String sysaddress = System.getProperty("dubbo.registry.address"); // 从启动参数读取
                if (sysaddress != null && sysaddress.length() > 0) {
                    address = sysaddress;
                }
                // 有效的地址
                if (address.length() > 0
                        && !RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                    Map map = new HashMap();
                    // 将各种配置对象,添加到 `map` 集合中。
                    appendParameters(map, application);
                    appendParameters(map, config);
                    // 添加 `path` `dubbo` `timestamp` `pid` 到 `map` 集合中。
                    map.put("path", RegistryService.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()));
                    }
                    // 若不存在 `protocol` 参数,默认 "dubbo" 添加到 `map` 集合中。
                    if (!map.containsKey("protocol")) {
                        if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) { // "remote"
                            map.put("protocol", "remote");
                        } else {
                            map.put("protocol", "dubbo");
                        }
                    }
                    // 解析地址,创建 Dubbo URL 数组。(数组大小可以为一)
                    List urls = UrlUtils.parseURLs(address, map);
                    // 循环 `url` ,设置 "registry" 和 "protocol" 属性。
                    for (URL url : urls) {
                        // 设置 `registry=${protocol}` 和 `protocol=registry` 到 URL
                        url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
                        url = url.setProtocol(Constants.REGISTRY_PROTOCOL);
                        // 添加到结果
                        if ((provider && url.getParameter(Constants.REGISTER_KEY, true)) // 服务提供者 && 注册 https://dubbo.gitbooks.io/dubbo-user-book/demos/subscribe-only.html
                                || (!provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) { // 服务消费者 && 订阅 https://dubbo.gitbooks.io/dubbo-user-book/demos/registry-only.html
                            registryList.add(url);
                        }
                    }
                }
            }
        }
        return registryList;
    }

精尽 Dubbo 源码分析 —— 服务暴露_第1张图片

2.3 doExportUrlsFor1Protocol

#doExportUrlsFor1Protocol(protocolConfig, registryURLs) 方法,基于单个协议,暴露服务。简化代码如下:

  /**
     * 基于单个协议,暴露服务
     *
     * @param protocolConfig 协议配置对象
     * @param registryURLs 注册中心链接对象数组
     */
    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List registryURLs) {
        // ... 【省略】 创建服务 URL 对象
        String scope = url.getParameter(Constants.SCOPE_KEY);
        // don't export when none is configured
        if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
            // 服务本地暴露
            // export to local if the config is not remote (export to remote only when config is remote)
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                exportLocal(url);
            }
            // 服务远程暴露
            // export to remote if the config is not local (export to local only when config is local)
            if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                // ... 【省略】远程暴露
            }
        }
        this.urls.add(url);
    }

2.3.1 exportLocal

#exportLocal(url) 方法,本地暴露服务。代码如下:


    /**
     * 本地暴露服务
     *
     * @param url 注册中心 URL
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private void exportLocal(URL url) {
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
            // 创建本地 Dubbo URL
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL) // injvm
                    .setHost(LOCALHOST) // 本地
                    .setPort(0); // 端口=0
            // 添加服务的真实类名,例如 DemoServiceImpl ,仅用于 RestProtocol 中。
            ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
            // 使用 ProxyFactory 创建 Invoker 对象
            // 使用 Protocol 暴露 Invoker 对象
            //实际上,Protocol 有两个 Wrapper 拓展实现类: ProtocolFilterWrapper、ProtocolListenerWrapper 。所以,#export(...) 方法的调用顺序是:Protocol$Adaptive => ProtocolFilterWrapper => ProtocolListenerWrapper => InjvmProtocol
            Exporter exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
            // 添加到 `exporters`
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
        }
    }

2.4 Protocol

涉及的 Protocol 类图如下:
精尽 Dubbo 源码分析 —— 服务暴露_第2张图片

2.4.1 Protocol$Adaptive

2.4.2 ProtocolFilterWrapper

实现 Protocol 接口,Protocol 的 Wrapper 拓展实现类,用于给 Invoker 增加过滤链

    public  Exporter export(Invoker 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));
    }

2.4.3 buildInvokerChain

创建带 Filter 链的 Invoker 对象。代码如下:

/**
     * 创建带 Filter 链的 Invoker 对象
     *
     * @param invoker Invoker 对象
     * @param key 获取 URL 参数名
     * @param group 分组
     * @param  泛型
     * @return Invoker 对象
     */
    private static  Invoker buildInvokerChain(final Invoker invoker, String key, String group) {
        Invoker last = invoker;
        // 获得过滤器数组
        List filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        // 倒序循环 Filter ,创建带 Filter 链的 Invoker 对象
        if (!filters.isEmpty()) {
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker next = last;
                last = new Invoker() {

                    @Override
                    public Class getInterface() {
                        return invoker.getInterface();
                    }

                    @Override
                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    @Override
                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

                    @Override
                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        System.out.println("group:" + group);
        for (Filter filter : filters) {
            System.out.println(filter.getClass());
        }
        return last;
    }

2.4.4 ProtocolListenerWrapper

实现 Protocol 接口,Protocol 的 Wrapper 拓展实现类,用于给 Exporter 增加 ExporterListener ,监听 Exporter 暴露完成和取消暴露完成

    public  Exporter export(Invoker invoker) throws RpcException {
        // 注册中心协议
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        // 暴露服务,创建 Exporter 对象
        Exporter exporter = protocol.export(invoker);
        // 获得 ExporterListener 数组
        List listeners = Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY));
        // 创建带 ExporterListener 的 Exporter 对象
        return new ListenerExporterWrapper(exporter, listeners);
    }
2.4.5 AbstractProtocol
public abstract class AbstractProtocol implements Protocol {

    /**
     * Exporter 集合
     *
     * key: 服务键 {@link #serviceKey(URL)} 或 {@link URL#getServiceKey()} 。
     *      不同协议会不同
     */
    protected final Map> exporterMap = new ConcurrentHashMap>();
2.4.6 InjvmProtocol

实现 AbstractProtocol 抽象类,Injvm 协议实现类。

    /**
     * 协议名
     */
    public static final String NAME = Constants.LOCAL_PROTOCOL;
    /**
     * 默认端口
     */
    public static final int DEFAULT_PORT = 0;
    /**
     * 单例。在 Dubbo SPI 中,被初始化,有且仅有一次。
     */
    private static InjvmProtocol INSTANCE;

    public InjvmProtocol() {
        INSTANCE = this;
    }

    public static InjvmProtocol getInjvmProtocol() {
        if (INSTANCE == null) {
            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(InjvmProtocol.NAME); // load
        }
        return INSTANCE;
    }

public  Exporter export(Invoker invoker) throws RpcException {
    return new InjvmExporter(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}
2.5 Exporter

本文涉及的 Exporter 类图如下:

精尽 Dubbo 源码分析 —— 服务暴露_第3张图片

2.5.1 AbstractExporter

实现 Exporter 接口,Exporter 抽象类。代码如下:

/**
 * AbstractExporter.
 *
 * Exporter 抽象类
 */
public abstract class AbstractExporter implements Exporter {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * Invoker 对象
     */
    private final Invoker invoker;
    /**
     * 是否取消暴露服务
     */
    private volatile boolean unexported = false;

    public AbstractExporter(Invoker invoker) {
        if (invoker == null)
            throw new IllegalStateException("service invoker == null");
        if (invoker.getInterface() == null)
            throw new IllegalStateException("service type == null");
        if (invoker.getUrl() == null)
            throw new IllegalStateException("service url == null");
        this.invoker = invoker;
    }

    @Override
    public Invoker getInvoker() {
        return invoker;
    }

    @Override
    public void unexport() {
        // 标记已经取消暴露
        if (unexported) {
            return;
        }
        unexported = true;
        // 销毁
        getInvoker().destroy();
    }

    public String toString() {
        return getInvoker().toString();
    }

}
2.5.2 InjvmExporter

/**
 * InjvmExporter
 *
 * Injvm Exporter 实现类
 */
class InjvmExporter extends AbstractExporter {

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

    InjvmExporter(Invoker invoker, String key, Map> exporterMap) {
        super(invoker);
        this.key = key;
        this.exporterMap = exporterMap;
        // 添加到 Exporter 集合
        exporterMap.put(key, this);
    }

    @Override
    public void unexport() {
        super.unexport();
        // 移除出 Exporter 集合
        exporterMap.remove(key);
    }

}

3 远程暴露

在本文中,我们来分享远程暴露服务。在 Dubbo 中提供多种协议( Protocol ) 的实现,大体流程一致,本文以 Dubbo Protocol 为例子,这也是 Dubbo 的默认协议。

相比本地暴露,远程暴露会多做如下几件事情:
启动通信服务器,绑定服务端口,提供远程调用。
向注册中心注册服务提供者,提供服务消费者从注册中心发现服务。

远程暴露服务的顺序图如下:

精尽 Dubbo 源码分析 —— 服务暴露_第4张图片

3.1 doExportUrlsFor1Protocol

涉及远程暴露服务的代码如下:

  // 服务远程暴露
            // 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.io/books/dubbo-user-book/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);
                }
            }
3.2 loadMonitor

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 map = new HashMap();
        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;
    }
3.3 Protocol

本文涉及的 Protocol 类图如下:

精尽 Dubbo 源码分析 —— 服务暴露_第5张图片

3.3.1 RegistryProtocol

RegistryProtocol ,实现 Protocol 接口,注册中心协议实现类。

/**
 * 绑定关系集合。
 *
 * 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> bounds = new ConcurrentHashMap>();
/**
 * Protocol 自适应拓展实现类,通过 Dubbo SPI 自动注入。
 */
private Protocol protocol;
/**
 * RegistryFactory 自适应拓展实现类,通过 Dubbo SPI 自动注入。
 */
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;
    }
3.3.2 export

本文涉及的 #export(invoker) 方法,代码如下:

  public  Exporter export(final Invoker originInvoker) throws RpcException {
        // 暴露服务
        // export invoker
        final ExporterChangeableWrapper 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.
        // 创建订阅配置规则的 URL
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
        // 创建 OverrideListener 对象,并添加到 `overrideListeners` 中
        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(exporter, originInvoker, overrideSubscribeUrl, registedProviderUrl);
    }
3.3.3 getRegistryUrl

调用 #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;
    }

该过程是红线部分 :

3.3.4 register

#register(registryUrl, registedProviderUrl) 方法,向注册中心注册服务提供者。代码如下:

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

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

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

3.4 DubboProtocol

实现 AbstractProtocol 抽象类,Dubbo 协议实现类。

3.4.1 export

本文涉及的 #export(invoker) 方法,代码如下:

    public  Exporter export(Invoker invoker) throws RpcException {
        URL url = invoker.getUrl();

        // 创建 DubboExporter 对象,并添加到 `exporterMap` 。
        // export service.
        String key = serviceKey(url);
        DubboExporter exporter = new DubboExporter(invoker, key, exporterMap);
        exporterMap.put(key, exporter);

        // TODO 【8005 sub】
        //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;
    }

3.4.2 openServer

启动通信服务器。代码如下:


/**
     * 启动通信服务器
     *
     * @param url URL
     */
    private void openServer(URL url) {
        // find server.
        String key = url.getAddress();
        //client 也可以暴露一个只有server可以调用的服务。
        //client can export a service which's only for server to invoke
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true); // isserver
        if (isServer) {
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
                serverMap.put(key, createServer(url));
            } else {
                // server supports reset, use together with override
                server.reset(url);
            }
        }
    }

3.4.3 createServer

创建并启动通信服务器。代码如下:

/**
     * 创建并启动通信服务器
     *
     * @param url URL
     * @return 服务器
     */
    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 ,即 DubboCountCodec
        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 supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
            if (!supportedTypes.contains(str)) {
                throw new RpcException("Unsupported client type: " + str);
            }
        }
        return server;
    }

3.5 Exporter

本文涉及的 Exporter 类图如下:

精尽 Dubbo 源码分析 —— 服务暴露_第6张图片

3.5.1 ExporterChangeableWrapper

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

 /**
     * exporter proxy, establish the corresponding relationship between the returned exporter and the exporter exported by the protocol, and can modify the relationship at the time of override.
     *
     * exporter 代理, 建立返回的 exporter 与 protocol export 出的 exporter 的对应关系,在 override 时可以进行关系修改.
     *
     * @param 
     */
    private class ExporterChangeableWrapper implements Exporter {

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

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

        public Invoker getOriginInvoker() {
            return originInvoker;
        }

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

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

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

    }
3.5.2 DestroyableExporter

实现 Exporter 接口,可销毁的 Exporter 实现类。

   /**
     * 可销毁的 Exporter
     *
     * @param  泛型
     */
    static private class DestroyableExporter implements Exporter {

        public static final ExecutorService executor = Executors.newSingleThreadExecutor(new NamedThreadFactory("Exporter-Unexport", true));

        private Exporter exporter;
        private Invoker originInvoker;
        private URL subscribeUrl;
        private URL registerUrl;

        public DestroyableExporter(Exporter exporter, Invoker originInvoker, URL subscribeUrl, URL registerUrl) {
            this.exporter = exporter;
            this.originInvoker = originInvoker;
            this.subscribeUrl = subscribeUrl;
            this.registerUrl = registerUrl;
        }

        public Invoker getInvoker() {
            return exporter.getInvoker();
        }

        public void unexport() {
            //
            Registry registry = RegistryProtocol.INSTANCE.getRegistry(originInvoker);
            try {
                registry.unregister(registerUrl);
            } catch (Throwable t) {
                logger.warn(t.getMessage(), t);
            }

            //
            try {
                NotifyListener listener = RegistryProtocol.INSTANCE.overrideListeners.remove(subscribeUrl);
                registry.unsubscribe(subscribeUrl, listener);
            } catch (Throwable t) {
                logger.warn(t.getMessage(), t);
            }

            //
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        int timeout = ConfigUtils.getServerShutdownTimeout();
                        if (timeout > 0) {
                            logger.info("Waiting " + timeout + "ms for registry to notify all consumers before unexport. Usually, this is called when you use dubbo API");
                            Thread.sleep(timeout);
                        }
                        exporter.unexport();
                    } catch (Throwable t) {
                        logger.warn(t.getMessage(), t);
                    }
                }
            });
        }
    }
3.5.3 DubboExporter

实现 AbstractExporter 抽象类,Dubbo Exporter 实现类。代码如下:

/**
 * DubboExporter
 */
public class DubboExporter extends AbstractExporter {

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

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

    @Override
    public void unexport() {
        // 取消暴露
        super.unexport();
        // 移除
        exporterMap.remove(key);
    }

}

你可能感兴趣的:(dubbo源码解析)