Dubbo剖析:(一)服务导出

服务导出主要从以下几个方面来阐述:

1.服务导出的目的

2.服务导出的入口

3.服务导出几个关键的概念

4.服务导出的流程

一:服务导出的目的

服务导出,其实就可以简单理解为服务暴露。服务提供者向注册中心注册服务,将服务实现类以服务接口的形式提供出去,这样可以就可以让服务消费者从注册中心中查看并订阅以及调用服务。

服务导出还有导出到本地和导出到远程两块内容,那就出现了直连和通过注册中心。

二:服务导出的入口
1.1:Spring配置以及ServiceBean映射

服务发布方,也就是提供者会在工程中有如下的spring 的配置,服务发布配置如下图:

 其中demoService为spring中配置服务的具体实现,所以就是spring 中的一个bean

<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>

而对于其他配置,spring容器在启动的过程中会解析自定义的schema元素 dubbo:service将其转换为实际的配置依赖于serviceBean,把服务暴露出去

 <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>

1.2 如何加载配置文件,并把配置文件转换为配置信息的

在spring启动加载过程中会进行扫描;如下图所示:

在DubboNamespaceHandler类类进行,把标签转换为每个配置类,主要是通过DubboBeanDefinitionParser来进行解析可得,这个解析类,是dubbo来实现的,主要为dubbo解析标签来使用。

 然后的话,其实整个标签解析以及转换为配置类的内容就已经完成,看起来还是比较简单的,但是还有一点就是,需要了解清楚的,那就是服务导出的关键入口位置,这其实也是涉及到dubbo和spring进行整合的尤其重要的一点,要想清楚的了解,那当然对spring框架也要有一定的理解,才能感受到是如何整合的,不然的话,看了也可能会一脸懵,因为学习一个知识的时候,一定要理解,不能为了看而看,多问自己几个为什么,好,下面步入serviceBean的正题。

1.2:ServiceBean

/**
 * ServiceBean
 * 实现和继承的关系
 * @export
 */
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
        ApplicationEventPublisherAware {
   
   

实现的InitializingBean和ApplicationListener接口,我的理解就是主要是和spring进行整合,并且在spring某个动作完成之后,进行初始化和加载dubbo所需要的内容。

看下面代码:此代码是spring 在属性赋值之后进行回调的方法,主要是用于每个配置类的信息存储到内存中

 // 这个是ioc容器初始化之前进行的属性赋值操作,了解这些过程,要对spring整个容器初始化也要有个详细的了解以及Spring框架的相关知识,
    // 不然也会存在很多疑惑,dubbo是如何和spring进行整合起来的
    @Override
    @SuppressWarnings({"unchecked", "deprecation"})
    public void afterPropertiesSet() throws Exception {
        if (getProvider() == null) {
            Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
            if (providerConfigMap != null && providerConfigMap.size() > 0) {
                Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
                if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
                        && providerConfigMap.size() > 1) { // backward compatibility
                    List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
                    for (ProviderConfig config : providerConfigMap.values()) {
                        if (config.isDefault() != null && config.isDefault().booleanValue()) {
                            providerConfigs.add(config);
                        }
                    }
                    if (!providerConfigs.isEmpty()) {
                        setProviders(providerConfigs);
                    }
                } else {
                    ProviderConfig providerConfig = null;
                    for (ProviderConfig config : providerConfigMap.values()) {
                        if (config.isDefault() == null || config.isDefault().booleanValue()) {
                            if (providerConfig != null) {
                                throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
                            }
                            providerConfig = config;
                        }
                    }
                    if (providerConfig != null) {
                        setProvider(providerConfig);
                    }
                }
            }
        }
        if (getApplication() == null
                && (getProvider() == null || getProvider().getApplication() == null)) {
            Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
            if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
                ApplicationConfig applicationConfig = null;
                for (ApplicationConfig config : applicationConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (applicationConfig != null) {
                            throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
                        }
                        applicationConfig = config;
                    }
                }
                if (applicationConfig != null) {
                    setApplication(applicationConfig);
                }
            }
        }
        if (getModule() == null
                && (getProvider() == null || getProvider().getModule() == null)) {
            Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
            if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
                ModuleConfig moduleConfig = null;
                for (ModuleConfig config : moduleConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (moduleConfig != null) {
                            throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
                        }
                        moduleConfig = config;
                    }
                }
                if (moduleConfig != null) {
                    setModule(moduleConfig);
                }
            }
        }
        if ((getRegistries() == null || getRegistries().isEmpty())
                && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().isEmpty())
                && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().isEmpty())) {
            Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
            if (registryConfigMap != null && registryConfigMap.size() > 0) {
                List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
                for (RegistryConfig config : registryConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        registryConfigs.add(config);
                    }
                }
                if (registryConfigs != null && !registryConfigs.isEmpty()) {
                    super.setRegistries(registryConfigs);
                }
            }
        }
        if (getMonitor() == null
                && (getProvider() == null || getProvider().getMonitor() == null)
                && (getApplication() == null || getApplication().getMonitor() == null)) {
            Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
            if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
                MonitorConfig monitorConfig = null;
                for (MonitorConfig config : monitorConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (monitorConfig != null) {
                            throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
                        }
                        monitorConfig = config;
                    }
                }
                if (monitorConfig != null) {
                    setMonitor(monitorConfig);
                }
            }
        }
        if ((getProtocols() == null || getProtocols().isEmpty())
                && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().isEmpty())) {
            Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
            if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
                List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
                for (ProtocolConfig config : protocolConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        protocolConfigs.add(config);
                    }
                }
                if (protocolConfigs != null && !protocolConfigs.isEmpty()) {
                    super.setProtocols(protocolConfigs);
                }
            }
        }
        if (getPath() == null || getPath().length() == 0) {
            if (beanName != null && beanName.length() > 0
                    && getInterface() != null && getInterface().length() > 0
                    && beanName.startsWith(getInterface())) {
                setPath(beanName);
            }
        }
        if (!isDelay()) {
            export();
        }
    }

关于onApplicationEvent的回调:这个是关键方法

ServiceBean除了继承dubbo自己的配置类ServiceConfig以外,还实现了一系列的spring接口用来参与到spring容器的启动以及bean创建过程中。其中包括ApplicationListener。

Spring容器ApplicationContext的启动最后一步会触发ContextRefreshedEvent事件, 而ServiceBean实现了ApplicationListener接口监听此事件,触发onApplicationEvent(ApplicationEvent event)方法,在这个方法中触发export方法来暴露服务。(这里使用的是spring监听器不是特别清楚的话,可以对spring的监听器进行学习和理解一下是如何做到的,这里就不对监听器进行过多的讲解了,我之前在github上面写过关于jdk和spring的监听器实现,有时间我把地址放在这,有兴趣的可以看一下)

 // onApplicationEvent 是一个事件响应方法,该方法会在收到 Spring 上下文刷新事件后执行服务导出操作
    //这个方法首先会根据条件决定是否导出服务,比务设置了延时如有些服导出,那么此时就不应该在此处导出。
    // 还有一些服务已经被导出了,或者当前服务被取消导出了,此时也不能再次导出相关服务。
    // 注意这里的 isDelay 方法,这个方法字面意思是“是否延迟导出服务”,返回 true 表示延迟导出,false 表示不延迟导出。
    // 但是该方法真实意思却并非如此,当方法返回 true 时,表示无需延迟导出。返回 false 时,表示需要延迟导出。
    // 与字面意思恰恰相反。  这里就是定义不好的地方,字面含义相反了
    // 还有需要记住的就是,在整整导出服务之前主要校验了三件东西  1.是否延迟导出 2.是否已经导出 3.是否取消导出

    // 关于前置的一些关于dubbo和spring的整合,以及前面的校验工作完成了,就可以好好了解一下导出服务的过程喽。滋滋滋
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 是否有延迟导出 && 是否已导出 && 是不是已被取消导出
        if (isDelay() && !isExported() && !isUnexported()) {
            if (logger.isInfoEnabled()) {
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            // 导出服务,这里是入口 在config包中
            // 服务调导出,主要的是从serviceBean中的onApplicationEvent
            // 实现spring的接口,spring进行上下文刷新后,发触发事件,这里会得到事件响应,然后就可以执行导出服务的操作了
            export();
        }
    }

包含的属性:

private transient String beanName;  //bean名称,对应xml中的id
private String interfaceName;  //接口名称,对应xml中的interface
private Class<?> interfaceClass;  //通过Class.forName(interfaceName)生成
private T ref;  //  接口实现类引用,对应xml中的ref
protected List<ProtocolConfig> protocols;  //协议列表
protected List<RegistryConfig> registries;  //注册中心列表
private final List<Exporter<?>> exporters;  //已发布服务列表
private final List<URL> urls;  //已发布服务地址列表
private static final Protocol protocol;
private static final ProxyFactory proxyFactory;

三:服务导出几个关键概念

3.1 Invoker执行体

// 可执行对象的抽象,能够根据方法的名称、参数得到相应的执行结果
public interface Invoker<T> extends Node {

    /**
     * get service interface.
     *
     * @return service interface.
     */
    Class<T> getInterface();

    /**
     * invoke.
     *
     * @param invocation
     * @return result
     * @throws RpcException
     */
    Result invoke(Invocation invocation) throws RpcException;

}

可执行对象的抽象,能够根据方法的名称、参数得到响应的执行结果。

   // 这个方法比较重要,是核心,也是执行体,具体如何组成的,要好好进行理解一下,目前先了解下具体的一个流程
    // 在dubbo中,Invoker是一个非常重要的模型。在服务提供端,以及服务引用端都会出现Invoker。Dubbo在官方文档中也有说明:
    // Invoker是一个实体域,它是dubbo的核心模型,其他模型都想它靠拢,或转换成它,它代表一个可执行体,可向他发起invoke调用
    // 它有可能是一个本地的实现,也可能是一个远程的实现,也可能是一个集群的实现
    // Invoker是由PRoxyFactory创建而成的,Dubbo默认的PRoxyFactory实现类是JavassistPRoxyFactory。
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

Invoker可分为三类:

  • AbstractProxyInvoker:本地执行类的Invoker,实际通过Java反射的方式执行原始对象的方法
  • AbstractInvoker:远程通信类的Invoker,实际通过通信协议发起远程调用请求并接收响应
  • AbstractClusterInvoker:多个远程通信类的Invoker聚合成的集群版Invoker,加入了集群容错和负载均衡策略

Invocation:包含了需要执行的方法和参数等重要信息,他有两个实现类RpcInvocation和MockInvocation。

3.2 ProxyFactory:


/**
 * ProxyFactory. (API/SPI, Singleton, ThreadSafe)
 * 服务接口代理抽象,用于生成一个接口的代理类,默认是javassist字节码生成
 */
@SPI("javassist")
public interface ProxyFactory {

    /**
     * create proxy.
     *
     * @param invoker
     * @return proxy
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;

    /**
     * create proxy.
     * 针对Client端,创建接口(如DemoService)的代理对象
     * @param invoker
     * @return proxy
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;

    /**
     * create invoker.
     *
     * @param <T>
     * @param proxy
     * @param type
     * @param url
     * @return invoker
     * 针对Server端,将服务对象(如DemoServiceImpl)包装成一个Invoker对象
     */
    // 这个方法比较重要,是核心,也是执行体,具体如何组成的,要好好进行理解一下,目前先了解下具体的一个流程
    // 在dubbo中,Invoker是一个非常重要的模型。在服务提供端,以及服务引用端都会出现Invoker。Dubbo在官方文档中也有说明:
    // Invoker是一个实体域,它是dubbo的核心模型,其他模型都想它靠拢,或转换成它,它代表一个可执行体,可向他发起invoke调用
    // 它有可能是一个本地的实现,也可能是一个远程的实现,也可能是一个集群的实现
    // Invoker是由PRoxyFactory创建而成的,Dubbo默认的PRoxyFactory实现类是JavassistPRoxyFactory。
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

服务接口代理抽象,用于生成一个接口的代理类。

getInvoker方法:针对于server端,将服务对象(如DemoServiceImpl)包装成一个Invoker对象。

getProxy方法:针对Client端,创建接口(如DemoService)的代理对象。

3.3 Exporter

public interface Exporter<T> {

    /**
     * get invoker.
     *
     * @return invoker
     */
    Invoker<T> getInvoker();

    /**
     * unexport.
     * <p>
     * <code>
     * getInvoker().destroy();
     * </code>
     */
    void unexport();

}

主要作用是维护invoker的生命周期,内部包含invoker或者ExporterMap

3.4 Protocol 协议

@SPI("dubbo")
public interface Protocol {

    /**
     * 获取缺省端口,当用户没有配置端口时使用。
     * @return 缺省端口
     */
    int getDefaultPort();

    /**
     * 暴露远程服务:
     * @param <T>     服务的类型
     * @param invoker 服务的执行体
     * @return exporter 暴露服务的引用,用于取消暴露
     * @throws RpcException 当暴露服务出错时抛出,比如端口已占用
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    /**
     * 引用远程服务:
     * @param <T>  服务的类型
     * @param type 服务的类型
     * @param url  远程服务的URL地址
     * @return invoker 服务的本地代理
     * @throws RpcException 当连接服务提供方失败时抛出
     */
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    /**
     * 释放协议
     */
    void destroy();

}

协议抽象接口。封装RPC调用。
exporter方法:暴露远程服务(用于服务端),就是将Invoker对象通过协议暴露给外部。
refer方法:引用远程服务(用于客户端),通过Clazz、url等信息创建远程的动态代理Invoker。

3.5: 关系流程图

1)ServiceConfig包含ProxyFactoryProtocol,通过SPI的方式注入生成;
2)ProxyFactory负责创建Invoker
3)Protocol负责通过Invoker生成Exporter,将服务启动并暴露;

四:服务导出流程解析

4.1 服务导出简单流程图

有些细节性的问题,后续还会进行详细去研究和讲解,比如关于getInvoker方法是如何实现的,openServer(),关于zk的订阅等

比如zk注册失败,如何进行取消,服务取消,如何从zk删除等。

4.2:DubboProtocol.export

总体做的事情就是:

  • 根据传入的Invoker创建一个DubboExporter并返回;
  • 使用HeaderExchanger创建交换层服务端HeaderExchangeServer。其底层依赖NettyTransporter创建网络层服务端NettyServer
  • NettyServer会完成启动网络服务并监听服务端口的工作;

4.3:服务导出的整体流程图:

后续关于一些细节性的问题,导出本地,导出远程,注册中心如何去搞的,等等细节性问题,后续有时间会后续持续发出,目前本章就先聊下的是服务导出的全链路的一个整体的过程,所谓学习框架的就是从面到线再到点,这篇文章呢也主要是了解到了每条线,关于点属于是更深层的一个认知了和理解了。

周末也不能闲着啊,有时间就了解和研究下,说下自己的心得喽

猜你喜欢

转载自blog.csdn.net/crossroads10/article/details/105326183