7、Dubbo的服务导出2之导出到远程

1、暴露服务到远程

上一篇文章分析了暴露服务到本地,6、Dubbo的服务导出1之导出到本地。接下来分析暴露服务到远程。

if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
    if (registryURLs != null && !registryURLs.isEmpty()) {
        for (URL registryURL : registryURLs) {
            // 添加动态参数,此动态参数是决定Zookeeper创建临时节点还是持久节点
            url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, 
                                                 registryURL.getParameter(Constants.DYNAMIC_KEY));
            String proxy = url.getParameter(Constants.PROXY_KEY);
            if (StringUtils.isNotEmpty(proxy)) {
                registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
            }
            // 步骤1)创建Invoker,这里创建Invoker逻辑和上面一样
            Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, 
                                                     registryURL.addParameterAndEncoded(
                                                       Constants.EXPORT_KEY, url.toFullString()));
            DelegateProviderMetaDataInvoker wrapperInvoker = 
                                               new DelegateProviderMetaDataInvoker(invoker, this);
            // 步骤2)暴露服务
            Exporter exporter = ServiceConfig.protocol.export(wrapperInvoker);
            exporters.add(exporter);
        }
    }
}
 /**
 * 下面分析步骤2,该方法两大核心逻辑,导出服务和注册服务,服务注册下篇文章分析
 */
@Override
public  Exporter export(final Invoker originInvoker) throws RpcException {
    // 1. 导出服务,export invoker,本篇文章仅分析第一步导出服务到远程
    final ExporterChangeableWrapper exporter = doLocalExport(originInvoker);
    // zookeeper://10.101.99.127:2181/com.alibaba.dubbo.registry.RegistryService
    // ?application=demo-provider&dubbo=2.0.2
    URL registryUrl = getRegistryUrl(originInvoker);

    // registry provider,默认返回ZookeeperRegistry实例
    final Registry registry = getRegistry(originInvoker);

    // dubbo://172.22.213.93:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true
    // &application=demo-provider&default.server=netty4&dubbo=2.0.2&generic=false
    // &interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=8140&side=provider
    final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker);

    // 不配置的话默认返回true
    boolean register = registeredProviderUrl.getParameter("register", true);
    ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);
    // 2.注册服务,这篇文章已经比较长了,决定将步骤2和步骤3新起一篇文章分析,服务暴露之后需要注册到注册中心
    if (register) {
        register(registryUrl, registeredProviderUrl);
        ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
    }

    // 3.数据更新订阅
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
    final OverrideListener overrideSubscribeListener = 
                                       new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
    return new DestroyableExporter(exporter, 
                                     originInvoker, overrideSubscribeUrl, registeredProviderUrl);
}
  private  ExporterChangeableWrapper doLocalExport(final Invoker originInvoker) {
    String key = getCacheKey(originInvoker);
    ExporterChangeableWrapper exporter = (ExporterChangeableWrapper) bounds.get(key);
    if (exporter == null) {
        synchronized (bounds) {
            exporter = (ExporterChangeableWrapper) bounds.get(key);
            if (exporter == null) {
                final Invoker invokerDelegete = new InvokerDelegete(originInvoker, 
                                                                  getProviderUrl(originInvoker));
                // 调用protocol的export方法导出服务,默认是采用Dubbo协议,对应DubboProtocol的export方法
                // 但是这里protocol.export()并不是先走DubboProtocol的export方法,而是先走
                // ProtocolListenerWrapper的wrapper方法
                // 因为ProtocolListenerWrapper对DubboProtocol做了一层包装,具体参考 
                // https://segmentfault.com/a/1190000020387196,核心方法protocal.export()
                exporter = new ExporterChangeableWrapper(
                                  (Exporter) protocol.export(invokerDelegete), originInvoker);
                bounds.put(key, exporter);
            }
        }
    }
    return exporter;
}
/**
 * 上述核心方法protocol.export()会先走到ProtocolListenerWrapper的export方法,该方法是在服务暴露上做了
   监听器功能的增强,也就是加上了监听器
 */
@Override
public  Exporter export(Invoker invoker) throws RpcException {
    // 如果是注册中心,则暴露该invoker
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    // 创建一个暴露者监听器包装类对象,暴露服务时这里的protocol是ProtocolFilterWrapper,这里用到了
    // Wrapper包装原有的DubboProtocol,可以参考https://segmentfault.com/a/1190000020387196
    return new ListenerExporterWrapper(protocol.export(invoker),
            Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                    .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
 /**
 * ProtocolFilterWrapper的export方法,该方法是在服务暴露上做了过滤器链的增强,也就是加上了过滤器
 */
@Override
public  Exporter export(Invoker invoker) throws RpcException {
    // 如果是注册中心,则直接暴露服务
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    // 服务提供侧暴露服务,这里通过buildInvokerChain形成了过滤器链
    return protocol.export(buildInvokerChain(invoker, 
                                               Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
/**
 * 该方法就是创建带Filter链的Invoker对象,倒序的把每一个过滤器串连起来,形成一个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);
    if (!filters.isEmpty()) {
        // 从最后一个过滤器开始循环,创建一个带有过滤器链的invoker对象
        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();
                }  
                // 关键在这里,调用下一个filter代表的invoker,把每一个过滤器串起来
                @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();
                }
            };
        }
    }
    return last;
}
// 经过两个Wrapper的export方法包装之后,走到DubboProtocol的export方法,这里是核心方法
public  Exporter export(Invoker invoker) throws RpcException {
    // url形如dubbo://172.22.213.93:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true
    // &application=demo-provider&bind.ip=172.22.213.93&bind.port=20880&dubbo=2.0.2&generic=false
    // /&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=648&qos.port=22222
    // &side=provider×tamp=1569585915258
    URL url = invoker.getUrl();
    // 获取服务标识,理解成服务坐标也行,由服务组名,服务名,服务版本号以及端口组成,key形如 
    // com.alibaba.dubbo.demo.DemoService:20880
    String key = serviceKey(url);
    // 创建DubboExporter
    DubboExporter exporter = new DubboExporter(invoker, key, exporterMap);
    // 将键值对放入缓存中
    exporterMap.put(key, exporter);

    // 本地存根相关代码, export an stub service for dispatching event
    // 删除,暂时还没有分析本地存根相关
    // 启动服务器,重点关注这里
    openServer(url);
    optimizeSerialization(url);
    return exporter;
}

// 根据URL值可以猜测,openServer方法就是启动Netty服务器,在172.22.213.93:20880端口上监听调用请求
openServer(url);
 /**
 * 在同一台机器上(单网卡),同一个端口上仅允许启动一个服务器实例,若某个端口上已有服务器实例,此时则调用reset方法
   重置服务器的一些配置
 */
private void openServer(URL url) {
    // 获取host:port,并将其作为服务器实例的key,用于标识当前的服务器实例,key形如172.22.213.93:20880
    String key = url.getAddress();
    //client can export a service which's only for server to invoke
    boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
    if (isServer) {
        ExchangeServer server = serverMap.get(key);
        if (server == null) {
            // 创建服务器实例,put之后serverMap形如<172.22.213.93:20880, HeaderExchangeServer>
            serverMap.put(key, createServer(url));
        } else {
            // 服务器已创建,则根据url中的配置重置服务器
            server.reset(url);
        }
    }
}
 private ExchangeServer createServer(URL url) {
    url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, 
                                                                      Boolean.TRUE.toString());
    // 添加心跳检测配置到URL中,enable heartbeat by default
    url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, 
                                                  String.valueOf(Constants.DEFAULT_HEARTBEAT));
    // 获取server参数,默认为netty,这里配置成了netty4,str就为netty4
    String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);

    // 通过SPI检测是否存在server参数所代表的Transporter拓展,不存在则抛出异常
    if (str != null && str.length() > 0 && !ExtensionLoader.
                                      getExtensionLoader(Transporter.class).hasExtension(str))
        throw new RpcException("Unsupported server type: " + str + ", url: " + url);

    // 添加编码解码器参数
    url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
    ExchangeServer server;
    try {
        // 创建 ExchangeServer,核心方法
        server = Exchangers.bind(url, requestHandler);
    } catch (RemotingException e) {
        throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
    }
    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;
}
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
    return getExchanger(url).bind(url, handler);
}

public static Exchanger getExchanger(URL url) {
    // 默认type就是header
    String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
    // 创建HeadExchanger
    return getExchanger(type);
}

public static Exchanger getExchanger(String type) {
    return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
}
  public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    // 用传输层绑定返回的server创建对应的信息交换服务端
    return new HeaderExchangeServer(Transporters.bind(url, 
                                        new DecodeHandler(new HeaderExchangeHandler(handler))));
}

你可能感兴趣的:(dubbo,java)