Dubbo系列讲解之服务发现3万字长文分享

Posted 波波烤鸭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo系列讲解之服务发现3万字长文分享相关的知识,希望对你有一定的参考价值。

服务注册的几个步骤

  对于RPC框架的服务注册,一般包含了如下的流程:

  • 加载服务提供者,可能是通过xml配置的,也可能是通过扫描注解的
  • 实例化服务提供者,并以服务接口作为key,实现类作为value存储到一个map容器中
  • 开启网络监听
  • 将服务提供者的地址路径(ip:port/服务名?参数等)注册到注册中心
  • 当网络监听接收到请求时,根据请求过来的服务名及参数等,从容器中获取到服务提供者实现,通过消费端调用时传送的方法名称反射调用服务提供者的相关方法

Dubbo源码分析

Dubbo与Spring的整合

  在实际的开发过程中,Dubbo大部分情况都是与Spring的生态进行整合使用的,所以在真正进入Dubbo的服务注册之前,我们需要先了解Dubbo是怎么将自己的环境嵌入到Spring生态中的。

  在Spring中使用Dubbo的方式有两种,一种是通过XML配置文件,一种是通过注解的方式,由于当下Spring Boot盛行,所以这里会比较深入的分析Dubbo在Spring Boot中的整合。不过其实两种方式最终的都是将Dubbo的相关组件注入到Spring 的容器中

  在Spring 中提供了一种NamespaceHandler的机制,用于对Spring标签的扩展,所以在Spring使用xml的方式时,Dubbo中会提供一个名为DubboNamespaceHandler的处理器,用于解析spring 的xml中的各种dubbo标签,并注入到容器中,这里不再深入。DubboNamespaceHandler源码如下:

public class DubboNamespaceHandler extends NamespaceHandlerSupport implements ConfigurableSourceBeanMetadataElement {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
    public void init() {
      // 将xml中的相关标签注入到spring中
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
        registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
        registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }

    /**
     * Override {@link NamespaceHandlerSupport#parse(Element, ParserContext)} method
     *
     * @param element       {@link Element}
     * @param parserContext {@link ParserContext}
     * @return
     * @since 2.7.5
     */
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        BeanDefinitionRegistry registry = parserContext.getRegistry();
        registerAnnotationConfigProcessors(registry);
        /**
         * @since 2.7.8
         * issue : https://github.com/apache/dubbo/issues/6275
         */
        registerCommonBeans(registry);
        BeanDefinition beanDefinition = super.parse(element, parserContext);
        setSource(beanDefinition);
        return beanDefinition;
    }

    /**
     * Register the processors for the Spring Annotation-Driven features
     *
     * @param registry {@link BeanDefinitionRegistry}
     * @see AnnotationConfigUtils
     * @since 2.7.5
     */
    private void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(registry);
    }
}

  接下来重点来看在Spring Boot中的整合。

  在Dubbo与Spring的整合,有两个入口可以让我们进入到Dubbo主见初始化的,第一种就是通过@EnableDubbo注解上的DubboComponentScan注解,它是一个@Import注解,Spring会通过引入一个DubboComponentScanRegistrar注册器在其registerBeanDefinitions方法上注入一个ServiceAnnotationBeanPostProcessor后置处理器。基于Spring的后置处理器原理,我们可以知道,它将会在bean实例化完成,初始化之前和之后各自产生回调,具体稍后再叙。注入方法如下:

private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
    builder.addConstructorArgValue(packagesToScan);
    builder.setRole(2);
    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
    BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}

  另一个入口就是Spring Boot的自动装配,在Dubbo中存在一个DubboAutoConfiguration类,该类通过Spring Boot中的自动装配原理注册到IOC容器中,同时该类是一个配置类,在该类中同时也装配了一个ServiceAnnotationBeanPostProcessor的bean,方法如下:

@ConditionalOnProperty(prefix = DUBBO_SCAN_PREFIX, name = BASE_PACKAGES_PROPERTY_NAME)
    @ConditionalOnBean(name = BASE_PACKAGES_PROPERTY_RESOLVER_BEAN_NAME)
    @Bean
    public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor(
            @Qualifier(BASE_PACKAGES_PROPERTY_RESOLVER_BEAN_NAME) PropertyResolver propertyResolver) {
        Set<String> packagesToScan = propertyResolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet());
        return new ServiceAnnotationBeanPostProcessor(packagesToScan);
    }

  接下来就进入到ServiceAnnotationBeanPostProcessor一探究竟。

  首先进入构造方法

 public ServiceAnnotationBeanPostProcessor(Set<String> packagesToScan) {
        super(packagesToScan);
 }

  这里将传入的packagesToScan往父类进行传递,由于它继承了ServiceClassPostProcessor,现在进入ServiceClassPostProcessor类的构造方法:

public ServiceClassPostProcessor(Set<String> packagesToScan) {
    this.packagesToScan = packagesToScan;
}

ServiceClassPostProcessor只是将传入的扫包路径赋值给packagesToScan

根据BeanPostProcessor的特性,现在进入到postProcessBeanDefinitionRegistry方法

@Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        // @since 2.7.5
        registerBeans(registry, DubboBootstrapApplicationListener.class);

        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
            registerServiceBeans(resolvedPackagesToScan, registry);
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
            }
        }

    }

该方法主要做了以下几件事:

  • 注册了一个DubboBootstrapApplicationListener监听,具体作用稍后再叙
  • 调用resolvePackagesToScan方法解析所有包名的路径。可能包名中存在一Placeholders的特殊定义
  • 调用registerServiceBeans方法进行注册

  具体怎么解析包路径不在本次讨论范围,所有就先不深入了,现在直接进入到registerServiceBeans方法中

private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

        scanner.setBeanNameGenerator(beanNameGenerator);

        // refactor @since 2.7.7
        serviceAnnotationTypes.forEach(annotationType -> {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        });

        for (String packageToScan : packagesToScan) {

            // Registers @Service Bean first
            scanner.scan(packageToScan);

            // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }

                if (logger.isInfoEnabled()) {
                    logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
                            beanDefinitionHolders +
                            " } were scanned under package[" + packageToScan + "]");
                }

            } else {

                if (logger.isWarnEnabled()) {
                    logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
                            + packageToScan + "]");
                }

            }

        }

    }

  该方法有主要做了以下几件事

  • 构建了一个DubboClassPathBeanDefinitionScanner对象,该对象继承自Spring的ClassPathBeanDefinitionScanner。在Spring中,ClassPathBeanDefinitionScanner是一个扫描程序,主要用来扫描Classpath下符合条件的对象,然后将对象注入到给定的registry中

  • 定义一个为Bean生成名称的BeanNameGenerator,这里生成的是AnnotationBeanNameGenerator这个策略

  • 将bean名称策略set到scanner中

  • 添加过滤Filter,这里遍历serviceAnnotationTypes,获取到所有的过滤条件,这里是基于注解的拦截,到serviceAnnotationTypes赋值的地方,可以看到。初始化了以下三种注解作为拦截

private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
        // @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
        DubboService.class,
        // @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service
        Service.class,
        // @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330
        com.alibaba.dubbo.config.annotation.Service.class
);
  • 遍历解析后的扫描的包,调用scanner.scan(packageToScan)注册所有标注了@Service的bean注入到ioc容器中

  • 调用findServiceBeanDefinitionHolders查找所有标注了@Service的Class封装成BeanDefinitionHolders,不管是否被@ComponentScan扫描

  • 如果beanDefinitionHolders存在元素,遍历beanDefinitionHolders,调用registerServiceBean注册

  将标注了@Service注解的bean注入到ioc容器不属于本次讨论内容,这里也不做详细说明
下面进入到findServiceBeanDefinitionHolders方法,了解一下该方法都返回了那些类型的BeanDefinitionHolder

private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders(
            ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry,
            BeanNameGenerator beanNameGenerator) {

        Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);

        Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());

        for (BeanDefinition beanDefinition : beanDefinitions) {

            String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
            BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
            beanDefinitionHolders.add(beanDefinitionHolder);

        }

        return beanDefinitionHolders;

    }
  • 首先扫描传入的packageToScan包下的所有的符合在scanner中定义的过滤注解的.class文件,封装成BeanDefinition
  • 遍历扫描到的beanDefinitions,通过名称策略,为Bean生成名称,同时用Bean和名称构建成BeanDefinitionHolder,加入到beanDefinitionHolders中,返回该集合

  beanDefinitionHolders的获取到这里就已经完成了,接下来进入到 registerServiceBean方法中,看看具体的注册流程

private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                 DubboClassPathBeanDefinitionScanner scanner) {

  // 通过beanDefinitionHolder中的BeanDefinition中保存的全类名通过Class.forName加载成class对象
    Class<?> beanClass = resolveClass(beanDefinitionHolder);

    Annotation service = findServiceAnnotation(beanClass);

    /**
     * The {@link AnnotationAttributes} of @Service annotation
     */
    AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);

    Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);

    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

    AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

    // ServiceBean Bean name
    String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);

    if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
        registry.registerBeanDefinition(beanName, serviceBeanDefinition);

        if (logger.isInfoEnabled()) {
            logger.info("The BeanDefinition[" + serviceBeanDefinition +
                    "] of ServiceBean has been registered with name : " + beanName);
        }

    } else {

        if (logger.isWarnEnabled()) {
            logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
                    "] of ServiceBean[ bean name : " + beanName +
                    "] was be found , Did @DubboComponentScan scan to same package in many times?");
        }

    }

}
  • 获取扫描到的类的字节码的class对象
  • 获取 beanClass上的注解,会跟初始化时的serviceAnnotationTypes属性中的注解进行匹配,返回匹配到的注解
  • 获取匹配到的注解service上的所有属性及其属性值serviceAnnotationAttributes
  • 获取扫描到的.class对象实现的接口的class对象interfaceClass
  • 获取组装BeanDefinitionHolder是为Bean生成的名称
  • 将所有参数传入到buildServiceBeanDefinition方法中,构建一个AbstractBeanDefinition的对象,这里我们暂时是不知道AbstractBeanDefinition保存的是哪个Bean的定义
  • 通过generateServiceBeanName方法构建一个ServiceBean的名称。
  • 检查是否存在重复名称的bean,如果不存在,则直接注入AbstractBeanDefinition的定义到ioc容器中

  现在我们先来探索一下在buildServiceBeanDefinition中构建的是一个什么Bean的定义,由于方法比较长,这里就不贴代码了,该方法的大概的流程就是创建了一个ServiceBean的BeanDefinition。然后组装前面解析到的注解的参数和获取到的实现类的接口等为ServiceBean的属性进行赋值,然后最后返回一个ServiceBean的BeanDefinition。

  然后再来看看ServiceBean的命名规则是怎么样的

private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) {
    ServiceBeanNameBuilder builder = create(interfaceClass, environment)
            .group(serviceAnnotationAttributes.getString("group"))
            .version(serviceAnnotationAttributes.getString("version"));
    return builder.build();
}

  可以看到,ServiceBean的命名规则是通过接口的全类名以及group,version等一起来保证唯一名称的,或许是长这样的ServiceBean:com.bobo.dubbo.api.HelloService:2.0.1这一系列操作下来,就是为了构建一个ServiceBean。而我们在DubboNamespaceHandler的方式中,也可以看到,最终也注入了一个ServiceBean。那么ServiceBean到底有何神奇之处呢?马上揭晓!进入到ServiceBean,看到其继承了ServiceConfig,同时实现了InitializingBean,DisposableBean,ApplicationContextAware,BeanNameAware,ApplicationEventPublisherAware等接口。而ServiceConfig又继承了AbstractConfig类,它是,比如Service,Refrence,application,Monitor等配置类的父类,我们进入AbstractConfig类,发现它存在一个@PostConstruct注解标注的方法,它会在spring的bean初始化完成之后执行,我们进入该方法

@PostConstruct
public void addIntoConfigManager() {
    ApplicationModel.getConfigManager().addConfig(this);
}

进入到ApplicationModel.getConfigManager()方法

public static ConfigManager getConfigManager() {
    return (ConfigManager) LOADER.getExtension(ConfigManager.NAME);
}

  看到这里,上一篇的SPI知识就排上了用场了,这是一个用来获取FrameworkExt接口的扩展实现的扩展点,同时ConfigManager.NAME指定了需要获取的扩展点的名称为config。所以就到FrameworkExt的实现类中查找一个名为config的扩展点实现即可得到。其实这里得到的就是ConfigManager本身。

  所以在addIntoConfigManager方法中,实际上是将当前的bean保存到了ConfigManager的对象中,最终保存到了ConfigManager的configsCache中。ConfigManager主要是用来管理Dubbo中的所有继承了AbstractConfig的配置

  而在ServiceBean中,我们并没有看到有任何任何有价值的东西,到这里看起来似乎前路已断,不知道怎么样入手了。

  此时突然想起来我们在进行扫包的一系列操作之前,貌似注册了一个监听器,是不是可以从监听器入手呢?

进入到前面注册的DubboBootstrapApplicationListener监听器中

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
        implements Ordered {

    /**
     * The bean name of {@link DubboBootstrapApplicationListener}
     *
     * @since 2.7.6
     */
    public static final String BEAN_NAME = "dubboBootstrapApplicationListener";

    private final DubboBootstrap dubboBootstrap;

    public DubboBootstrapApplicationListener() {
        this.dubboBootstrap = DubboBootstrap.getInstance();
    }

    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }

    private void onContextClosedEvent(ContextClosedEvent event) {
        dubboBootstrap.stop();
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }
}

  可以看到,该监听器监听了容器的容器的刷新和关闭,我们前面的操作已经将ServiceBean注入到了ioc容器中,根据ioc的容器初始化的几个周期,可以知道在Refreshd容器时,我们所有的服务提供者对应的ServiceBean已经全部装载到了容器中。

  继续往下,当产生ContextRefreshedEvent事件时,调用了onContextRefreshedEvent方法,该方法中调用dubboBootstrap.start();

  到这里,跟Spring相关的东西已经走完了,下面做一个总结