Dubbo源码通~服务暴露之远程暴露

Dubbo服务暴露~远程暴露

远程暴露有两种情况:

  • 配置了注册中心的,需要将服务注册到注册中心
  • 未配置注册中心的,仅仅将服务按照远程协议暴露出来

1、未配置注册中心(用于服务消费者直连服务提供者)

具体步骤如下:

  1. 创建Invoker的步骤和Injvm的方式相同;
  2. 创建DelegateProviderMetaDataInvoker对象,持有Invoker和ServiceConfig对象;
  3. 创建Exporter对象,步骤和Injvm相同。

和本地暴露的区别:

  • Invoker对象是: DelegateProviderMetaDataInvoker
  • Exporter中的Protocol扩展类不同(根据服务暴露配置或者默认)

2、配置了注册中心

包含两个步骤:服务导出和服务注册

// NOTE1: 创建Invoker
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

// NOTE: DelegateProviderMetaDataInvoker 用于持有 Invoker 和 ServiceConfig
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

// NOTE2: 导出服务,并生成 Exporter
Exporter<?> exporter = protocol.export(wrapperInvoker);

2.1、创建Invoker

  • 与无注册中心服务暴露创建Invoker的过程基本相同路径:ProxyFactory$Adaptive => StubProxyFactoryWrapper => JavassistProxyFactory => Wrapper => AbstractProxyInvoker => DelegateProviderMetaDataInvoker。
  • 内部的URL配置协议头为“registry
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&......&pid=10421&qos.port=22222®istry=zookeeper×tamp=1561639865358

2.2、创建Export并暴露

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

2.2.1、调用主路径

路径:由两个小链路构成一个大链路

  • 链路1: Protocol$Adaptive => ProtocolFilterWrapper => ProtocolListenerWrapper => RegistryProtocol【RegistryFactory$Adaptive => ProxyFactory$Adaptive => Protocol$Adaptive => Cluster$Adaptive】=> Protocol$Adaptive
  • 链路2: Protocol$Adaptive => ProtocolFilterWrapper => ProtocolListenerWrapper => DubboProtocol => DubboExporter => 服务注册
  • 通信服务器启动:DubboProtocol => ExchangeServer => Transporters => NettyServer

  • 创建RegistryProtocol扩展类时的内部属性关系图 (大佬的图)
    Dubbo源码通~服务暴露之远程暴露_第1张图片
  • 注册中心服务暴露流程图

2.2.2、调用步骤描述

  • 首先,传入的是注册中心的 URL(registryURL) ,通过 Protocol$Adaptive 获取到的是 RegistryProtocol 对象。
  • 其次,RegistryProtocol 会在其 #export(…) 方法中,使用服务提供者的 URL ( 即注册中心的 URL 的 export 参数值),再次调用 Protocol$Adaptive 获取到的是 DubboProtocol 对象,创建DubboExporter。
  • 在DubboProtocol中启动通信服务器(默认为NettyServer)。
  • 最后,向注册中心注册服务提供者的URL。

2.3、服务导出和通信服务器启动(走代码)

1、RegistryProtocol

  • 调用 doLocalExport 导出服务(对应NOTE 1
  • 向注册中心注册服务(对应NOTE 2
  • 向注册中心进行订阅 override 数据
  • 创建并返回 DestroyableExporter(对应NOTE 3
//☆☆--RegistryProtocol
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    // NOTE: 获取注册中心 URL
    URL registryUrl = getRegistryUrl(originInvoker);
    //NOTE: 服务暴露URL
    URL providerUrl = getProviderUrl(originInvoker);
    
    //NOTE:  获取订阅 URL(provider开头)
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);

    //NOTE: 使用 OverrideListener 对象,订阅配置规则
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);

    //NOTE 1:  创建Exporter,暴露服务
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

    // NOTE: 根据 URL 加载 Registry 实现类,比如 ZookeeperRegistry(这里不是SPI 机制)
    final Registry registry = getRegistry(originInvoker);
    // NOTE: 获取已注册的服务提供者 URL
    final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
    //NOTE: 向本地注册表,注册服务提供者
    ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
            registryUrl, registeredProviderUrl);
    //to judge if we need to delay publish
    boolean register = registeredProviderUrl.getParameter("register", true);
    if (register) {
        // NOTE 2: 向注册中心注册服务
        register(registryUrl, registeredProviderUrl);
        providerInvokerWrapper.setReg(true);
    }
    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
    
    // NOTE 3: 创建并返回 DestroyableExporter
    return new DestroyableExporter<>(exporter);
}

2、RegistryProtocol.doLocalExport

/**
 * 服务导出
 */
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
//NOTE:缓存
    String key = getCacheKey(originInvoker);
    ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
    if (exporter == null) {
        synchronized (bounds) {
            exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
            if (exporter == null) {
                //NOTE:创建 Invoker 为委托类对象,将providerUrl和originInvoker封装
                final Invoker<?> invokerDelegete = new InvokerDelegate<T>(originInvoker, providerUrl);
                //NOTE: DubboProtocol export创建Exporter,进行服务暴露
                /**
                 * 使用【创建的 Exporter 对象】+【originInvoker】,创建 ExporterChangeableWrapper 对象。这样,originInvoker 就和 Exporter 对象,形成了绑定的关系
                 */
                exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
                bounds.put(key, exporter);
            }
        }
    }
    return exporter;
}

3、DubboProtocol.export(invokerDelegete)

  • 创建DubboExporter(对应NOTE 1
  • 创建并启动通信服务器
  • 优化序列化
//☆☆--DubboProtocol
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    //对应的是providerUrl
    URL url = invoker.getUrl();
    // NOTE: 获取服务标识,由服务组名,服务名,服务版本号以及端口组成
    String key = serviceKey(url);

    //NOTE 1: 创建DubboExporter对象
    DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
    // NOTE: 将  键值对放入缓存中
    exporterMap.put(key, exporter);
    ......
    
    // NOTE 2 : 启动服务器(netty 通讯)
    openServer(url);
    
    // NOTE 3: 优化序列化
    optimizeSerialization(url);
    return exporter;
}
  • 创建并启动通信服务器
private void openServer(URL url) {
    // NOTE: 取 host:port,并将其作为服务器实例的 key,用于标识当前的服务器实例。在同一台机器上,同一个端口上仅允许启动一个服务器实例。
    String key = url.getAddress();
    boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
    if (isServer) {
        ExchangeServer server = serverMap.get(key);
        if (server == null) {
            synchronized (this) {
                server = serverMap.get(key);
                if (server == null) {
                    serverMap.put(key, createServer(url));
                }
            }
        } else {
            server.reset(url);
        }
    }
}

/**
 * NOTE: 创建通信服务器(服务层)
 *  1、检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常
 *  2、创建ExchangerServer服务器实例
 *  3、检测是否支持 client 参数所表示的 Transporter 拓展
 */
private ExchangeServer createServer(URL url) {
    url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
    //NOTE: 添加心跳检测配置到 url 中,默认60秒
    url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
    // NOTE: 获取 server 参数,默认为 netty
    String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);

    // NOTE: 通过 SPI 检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常
    ......代码省略

    // NOTE: 添加编码解码器参数(默认为'dubbo')
    url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
    ExchangeServer server;
    try {
        //NOTE: 创建ExchangerServer服务器
        server = Exchangers.bind(url, requestHandler);
    } catch (RemotingException e) {
        throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
    }
    
    //是否支持 client 参数所表示的 Transporter 拓展
    ......省略client的校验代码
    return server;
}
  • Exchangers.bind(url, requestHandler)
//☆☆--Exchangers
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
    //获得Exchanger的扩展实现类:HeaderExchanger
    Exchanger exchanger =  ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
    return exchanger.bind(url, handler);
}

//☆☆--HeaderExchanger
public class HeaderExchanger implements Exchanger {
    @Override
    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
        // NOTE: 这里包含了多个调用,分别如下:
        // 1. 创建 HeaderExchangeHandler 对象
        // 2. 创建 DecodeHandler 对象
        // 3. 通过 Transporters 构建 Server 实例(默认NettyServer,NettyTransporter)
        // 4. 创建 HeaderExchangeServer 对象
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }   
}

//☆☆--Transporters
public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
    if (handlers.length == 1) {
        handler = handlers[0];
    } else {//如果 handlers 元素数量大于1,则创建 ChannelHandler 分发器
        handler = new ChannelHandlerDispatcher(handlers);
    }
    // NOTE: 获取自适应 Transporter 实例,并调用实例方法(默认为NettyTransporter)
    return getTransporter().bind(url, handler);
}

//☆☆--NettyTransporter
public class NettyTransporter implements Transporter {
    @Override
    public Server bind(URL url, ChannelHandler listener) throws RemotingException {
        // NOTE: 创建Netty Server 并调用内部的doOpen()方法启动服务 
        return new NettyServer(url, listener);
    }
}

2.4、服务注册(走代码)

调用DubboProtocol创建Exporter后,会根据

exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
  • 获得注册中心实例
  • 向注册中心注册服务提供者URL

2.4.1、获得注册中心实例(工厂)

根据registryUrl获取对应的Registry扩展类(以ZookeeperRegistry为例)
Dubbo源码通~服务暴露之远程暴露_第2张图片

//RegistryProtocol
Registry registry = registryFactory.getRegistry(registryUrl);

//根据registryUrl获得对应的Registry:ZookeeperRegistry、DubboRegistry等
private Registry getRegistry(final Invoker<?> originInvoker) {
    URL registryUrl = getRegistryUrl(originInvoker);
    return registryFactory.getRegistry(registryUrl);
}

//☆☆--AbstractRegistryFactory
public Registry getRegistry(URL url) {
    url = url.setPath(RegistryService.class.getName())
            .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
            .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
    String key = url.toServiceStringWithoutResolving();
    // 创建Registry时,需要加锁
    LOCK.lock();
    try {
        Registry registry = REGISTRIES.get(key);
        if (registry != null) {
            return registry;
        }
        //创建Registry
        registry = createRegistry(url);
        if (registry == null) {
            throw new IllegalStateException("Can not create registry " + url);
        }
        REGISTRIES.put(key, registry);
        return registry;
    } finally {
        // Release the lock
        LOCK.unlock();
    }
}
  • 创建注册中心:ZookeeperRegistry
public class ZookeeperRegistryFactory extends AbstractRegistryFactory {

    private ZookeeperTransporter zookeeperTransporter;

    public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
        this.zookeeperTransporter = zookeeperTransporter;
    }

    @Override
    public Registry createRegistry(URL url) {
     //创建ZookeeperRegistry(内部默认的zkClient为:CuratorZookeeperClient)
        return new ZookeeperRegistry(url, zookeeperTransporter);
    }

}

2.4.2、向注册中心注册服务

注册中心注册服务其实就是将服务提供者的信息,封装到URL中,在注册中心(zk)上创建一个个节点。

 public void register(URL registryUrl, URL registeredProviderUrl) {
    Registry registry = registryFactory.getRegistry(registryUrl);
    // 注册服务
    registry.register(registeredProviderUrl);
}

Dubbo源码通~服务暴露之远程暴露_第3张图片

  • 主路径 :RegistryProtocol => FailbackRegistry => ZookeeperRegistry => CuratorZookeeperClient
//☆☆---ZookeeperRegistry
 public void doRegister(URL url) {
    try {
        // NOTE:  通过 Zookeeper 客户端创建节点,节点路径由 toUrlPath 方法生成,路径格式如下:
        //  ${group}/${serviceInterface}/providers/${url}
        // NOTE:  比如: /dubbo/org.apache.dubbo.DemoService/providers/dubbo%3A%2F%2F127.0.0.1....
        zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
    } catch (Throwable e) {
        throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}
  • zkClient中创建节点
//AbstractZookeeperClient
public void create(String path, boolean ephemeral) {
    if (!ephemeral) {
        if (checkExists(path)) {
            return;
        }
    }
    //NOTE: 层层解析,创建zk节点
    int i = path.lastIndexOf('/');
    if (i > 0) {
        create(path.substring(0, i), false);
    }
    //NOTE: 是否创建临时节点
    if (ephemeral) {
        createEphemeral(path);
    } else {
        createPersistent(path);
    }
}

//CuratorZookeeperClient
public void createEphemeral(String path) {
    try {
    // 通过 Curator 框架创建节点
client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
    } catch (NodeExistsException e) {
    } catch (Exception e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
}

3、总结

服务远程暴露,创建Invoker简单,但后续的服务导出流程比较长,主要在创建Exporter和服务注册过程很复杂。而且在RegistryProtocol和DubboProtocol执行逻辑中,都会用类似ProtocolFilterWrapper或者ProtocolListenerWrapper这种封装类来实现AoP增加一些功能。在服务注册过中,路径特别深,有一些我还没有深入,比如ExchangerServer的创建、Transporter的创建、Registry的创建细节。理解服务远程暴露主要包含:

  • 将服务封装成一个Exoprter进行服务导出;
  • 将服务提供者关联的URL配置信息,在注册中心按照目录结构创建一个个节点。(节点的信息是按照GIV的形式来构建的)

4、附件

RegistryProtocol内部注入的属性:RegistryFactory、Cluster

4.1、RegistryFactory$Adaptive

package org.apache.dubbo.registry;
import org.apache.dubbo.common.extension.ExtensionLoader;

public class RegistryFactory$Adaptive implements org.apache.dubbo.registry.RegistryFactory {

    public org.apache.dubbo.registry.Registry getRegistry(org.apache.dubbo.common.URL arg0) {
        if (arg0 == null) {
            throw new IllegalArgumentException("url == null");
        }

        org.apache.dubbo.common.URL url = arg0;
        String extName = ((url.getProtocol() == null) ? "dubbo" : url.getProtocol());

        if (extName == null) {
            throw new IllegalStateException(
                "Fail to get extension(org.apache.dubbo.registry.RegistryFactory) name from url(" +
                url.toString() + ") use keys([protocol])");
        }

        org.apache.dubbo.registry.RegistryFactory extension = (org.apache.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.registry.RegistryFactory.class).getExtension(extName);
        return extension.getRegistry(arg0);
    }
}

4.2、Cluster$Adaptive

package org.apache.dubbo.rpc.cluster;
import org.apache.dubbo.common.extension.ExtensionLoader;

public class Cluster$Adaptive implements org.apache.dubbo.rpc.cluster.Cluster {

    public org.apache.dubbo.rpc.Invoker join(org.apache.dubbo.rpc.cluster.Directory arg0)
        throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) {
            throw new IllegalArgumentException(
                "org.apache.dubbo.rpc.cluster.Directory argument == null");
        }

        if (arg0.getUrl() == null) {
            throw new IllegalArgumentException(
                "org.apache.dubbo.rpc.cluster.Directory argument getUrl() == null");
        }

        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("cluster", "failover");

        if (extName == null) {
            throw new IllegalStateException(
                "Fail to get extension(org.apache.dubbo.rpc.cluster.Cluster) name from url(" +
                url.toString() + ") use keys([cluster])");
        }

        org.apache.dubbo.rpc.cluster.Cluster extension = (org.apache.dubbo.rpc.cluster.Cluster) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.cluster.Cluster.class).getExtension(extName);

        return extension.join(arg0);
    }
}

你可能感兴趣的:(服务暴露,远程暴露,Dubbo源码通)