Dubbo: 在springboot中的启动过程

2021-09-15  本文已影响0人  布拉德老瓜

版本

SpringBoot中dubbo启动过程

1. @DubboService注解的bean是如何被注册到容器中的

SpringBoot在启动时,通过ConfigurationClassPostProcessor.postProcessBeanFactory完成对依赖jar包中XxAutopConfiguration类的注册,自然DubboAutoConfiguration也会被注册到容器内部。

DubboAutoConfiguration中,定义了一个ServiceClassPostProcessor ,同样会被注册到容器内。

@Configuration
@AutoConfigureAfter({DubboRelaxedBindingAutoConfiguration.class})
@EnableConfigurationProperties({DubboConfigurationProperties.class})
@EnableDubboConfig
public class DubboAutoConfiguration implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {
    public DubboAutoConfiguration() {
    }

    @ConditionalOnProperty(
        prefix = "dubbo.scan.",
        name = {"base-packages"}
    )
    @ConditionalOnBean(
        name = {"dubbo-service-class-base-packages"}
    )
    @Bean
    public ServiceClassPostProcessor serviceClassPostProcessor(@Qualifier("dubbo-service-class-base-packages") Set<String> packagesToScan) {
        return new ServiceClassPostProcessor(packagesToScan);
    }

ServiceClassPostProcessor 实现了BeanDefinitionRegistryPostProcessor接口,同样他也是一个BeanFactoryPostProcessor。

public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware {

在SpringBoot刷新容器,调用所有BeanFactoryPostProcessors时,对BeanDefinitionRegistryPostProcessor,会去调用其postProcessBeanDefinitionRegistry方法。

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 注册监听器,容器启动各个阶段向监听器发送消息
        BeanRegistrar.registerInfrastructureBean(registry, "dubboBootstrapApplicationListener", DubboBootstrapApplicationListener.class);
        Set<String> resolvedPackagesToScan = this.resolvePackagesToScan(this.packagesToScan);
        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
            // 从待扫描的包中获取ServiceBean,然后注册到容器
            this.registerServiceBeans(resolvedPackagesToScan, registry);
        } else if (this.logger.isWarnEnabled()) {
            this.logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
        }

    }
    private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, this.environment, this.resourceLoader);
        BeanNameGenerator beanNameGenerator = this.resolveBeanNameGenerator(registry);
        scanner.setBeanNameGenerator(beanNameGenerator);
        serviceAnnotationTypes.forEach((annotationType) -> {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        });
        Iterator var5 = packagesToScan.iterator();

        // 获取迭代器,遍历待扫描的packages,获取ServiceBean信息,封装beanDefinition并注册到IOC容器
        while(true) {
            while(var5.hasNext()) {
                String packageToScan = (String)var5.next();
                scanner.scan(new String[]{packageToScan});
                Set<BeanDefinitionHolder> beanDefinitionHolders = this.findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
                if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
                    Iterator var8 = beanDefinitionHolders.iterator();

                    while(var8.hasNext()) {
                        BeanDefinitionHolder beanDefinitionHolder = (BeanDefinitionHolder)var8.next();
                        this.registerServiceBean(beanDefinitionHolder, registry, scanner);
                    }

                    if (this.logger.isInfoEnabled()) {
                        this.logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " + beanDefinitionHolders + " } were scanned under package[" + packageToScan + "]");
                    }
                } else if (this.logger.isWarnEnabled()) {
                    this.logger.warn("No Spring Bean annotating Dubbo's @Service was found under package[" + packageToScan + "]");
                }
            }

            return;
        }

至此,便将ServiceBean注册进了Spring IOC容器。至于对象和代理对象的创建,那是后话了。

2. DubboService是如何对外暴露其url的

我们知道DubboService会被注册到注册中心,最终的结果是:将服务名、服务对外暴露的url等信息通过网络请求发送到注册中心,那么对外暴露的时机是什么时候?它又是如何做到这件事情的?
关于时机,它应该在容器刷新完成之后将所有DubboService对外暴露,那么如何感知到容器刷新呢?SpringBoot中可以注册listener,容器开始启动、启动完成等事件会通知注册进来的listener。
ServiceClassPostProcessor 除了向容器注册ServiceBean之外,还注册了一个监听器:DubboBootstrapApplicationListener,当感知到容器刷新完成和关闭事件时,做出相应处理,在这里关注刷新完成该如何处理。

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

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

启动DubboBootStrap

    public DubboBootstrap start() {
            // ... 省略诸多初始化代码

            // 服务暴露
            this.exportServices();
            if (!this.isOnlyRegisterProvider() || this.hasExportedServices()) {
                this.exportMetadataService();
                this.registerServiceInstance();
            }

           // 服务引用
            this.referServices();
            if (this.asyncExportingFutures.size() > 0) {
                (new Thread(() -> {
                    try {
                        this.awaitFinish();
                    } catch (Exception var2) {
                        this.logger.warn(NAME + " exportAsync occurred an exception.");
                    }

                    this.ready.set(true);
                    if (this.logger.isInfoEnabled()) {
                        this.logger.info(NAME + " is ready.");
                    }

                })).start();
            } else {
                this.ready.set(true);
                if (this.logger.isInfoEnabled()) {
                    this.logger.info(NAME + " is ready.");
                }
            }
            // log ... 
        }
        return this;
    }
    private void exportServices() {
        this.configManager.getServices().forEach((sc) -> {
            ServiceConfig serviceConfig = (ServiceConfig)sc;
            serviceConfig.setBootstrap(this);
            if (this.exportAsync) {
                ExecutorService executor = this.executorRepository.getServiceExporterExecutor();
                Future<?> future = executor.submit(() -> {
                    sc.export();
                    this.exportedServices.add(sc);
                });
                this.asyncExportingFutures.add(future);
            } else {
                sc.export();
                this.exportedServices.add(sc);
            }
        });
    }

至于Service如何暴露:如下图

export过程
参考:https://mergades.blog.csdn.net/article/details/109338146
上面注册的ServiceBean实际上就继承了ServiceConfig。
配合下图右半部分:
DubboBootStrap在exportServices中获取ServiceConfigs。迭代每个ServiceConfig在export的时候创建URL, 通过代理工厂创建invoker,最后由Protocol完成导出。在导出时,分别要向本地和注册中心分别暴露。本地导出时,通过proxyFactory获取invoker,然后由InJVMProtocol完成导出工作,做这件事情的目的是:有时,一个服务既是provider,又是consumer。如果进程内调用该服务,那么它不应该像其他进程一样通过网络请求来进行调用,而是在进程内通信,减少IO带来的开销。远程暴露就比较容易理解,根据invoker的url做encode,然后想注册中心发起请求。
image.png

最后服务暴露到底对外暴露了个啥呢?其实就是invoker的url,当consumer发起服务调用的时候,发起请求,当provider接收到请求之后,将请求携带的信息(接口、方法名、参数数组...)封装为Invocation对象,分派到相应的invoker,通过invoker.invoke(invocation)完成调用。
另外敖丙的文章也还不错,Dubbo系列之服务暴露过程

3. 服务引入及远程调用

从springboot解析@DubboReference开始讲起。
DubboAutoConfiguration被@EnableDubboConfig注解,通过该注解引入了DubboConfigConfigurationRegistrar类。

@EnableDubboConfig
...
@Import({DubboConfigConfigurationRegistrar.class})

DubboConfigConfigurationRegistrar::registerBeanDefinitions(args)

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //...省略一些代码
        DubboBeanUtils.registerCommonBeans(registry);
    }
public interface DubboBeanUtils {
    static void registerCommonBeans(BeanDefinitionRegistry registry) {
        BeanRegistrar.registerInfrastructureBean(registry, "referenceAnnotationBeanPostProcessor", ReferenceAnnotationBeanPostProcessor.class);
        // ... 省略
    }
}

ReferenceAnnotationBeanPostProcessor是一个InstantiationAwareBeanPostProcessorAdapter,在对象实例化后,填充属性中,会调用其postProcessPropertyValues

public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements ApplicationContextAware {
    // ... 省略

    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
        InjectionMetadata metadata = this.findInjectionMetadata(beanName, bean.getClass(), pvs);

        try {
            // 这里的metadata就是AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement对象
            metadata.inject(bean, beanName, pvs);
            return pvs;
        } catch (BeanCreationException var7) {
            throw var7;
        } catch (Throwable var8) {
            throw new BeanCreationException(beanName, "Injection of @" + this.getAnnotationType().getSimpleName() + " dependencies is failed", var8);
        }
    }
}

    public class AnnotatedFieldElement extends InjectedElement {
        // ...

        protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
            Class<?> injectedType = this.resolveInjectedType(bean, this.field);
            Object injectedObject = AbstractAnnotationBeanPostProcessor.this.getInjectedObject(this.attributes, bean, beanName, injectedType, this);
            ReflectionUtils.makeAccessible(this.field);
            this.field.set(bean, injectedObject);
        }
}

最终到达referenceBean.get()方法,返回代理对象,并注入到目标对象的field。

    protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType, InjectedElement injectedElement) throws Exception {
        String referencedBeanName = this.buildReferencedBeanName(attributes, injectedType);
        String referenceBeanName = this.getReferenceBeanName(attributes, injectedType);
        ReferenceBean referenceBean = this.buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
        boolean localServiceBean = this.isLocalServiceBean(referencedBeanName, referenceBean, attributes);
        this.prepareReferenceBean(referencedBeanName, referenceBean, localServiceBean);
        this.registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);
        this.cacheInjectedReferenceBean(referenceBean, injectedElement);
        return referenceBean.get();
    }

如果抛开SpringBoot如何注入被@DubboReference注解的Bean,可以说referenceBean.get()就是服务引用的入口。

    // ... 省略诸多代码
    private transient volatile T ref;
    // 线程安全单例
    public synchronized T get() {
        if (this.destroyed) {
            throw new IllegalStateException("The invoker of ReferenceConfig(" + this.url + ") has already destroyed!");
        } else {
            if (this.ref == null) {
                this.init();
            }

            return this.ref;
        }
    }
 // ...
}

后面的事情就是Dubbo内部要完成的了,大概过程如这样:

image.png
ref:https://zhuanlan.zhihu.com/p/224938929
上一篇下一篇

猜你喜欢

热点阅读