Dubbo服务的注册流程
服务发布步骤
- 注解
@DubboService( loadbalance = "random", cluster = "failover", retries = 2)
- 注解扫描
@DubboComponentScan
思考
首先需要扫描注解,在扫描的过程中,可以拿到注解中的数据(注解的目的其实是做一个标记的功能,可以通过不同的标记去对一些类做一个分类,具体扫描哪一个注解取决于对其的关注度),然后解析注解获取对应的配置。
解析完成后,需要去发布服务,像Dubbo是基于URL驱动的,其会将所有的配置信息配置在URL的地址上,所以这一步主要做的就是URL的组装。
后面要做的事情就是将其注册到zookeeper上(相当于把服务端的相关配置信息和服务的地址信息都会保存到第三方的平台上)到了第三方平台之后,如果我的客户端要去调用的时候,可以通过第三方平台知道服务端的调用机制是什么,这些都可以在URL上识别到。
接下来就是启动服务了,根据URL中配置的协议、配置的端口去发布对应的服务。
Dubbo源码分析
Dubbo发布其实有两种形式,其实从前面介绍的博客上来说
分为 xml形式 dubbo:service 和 注解形式 @DubboService/ @Service
Dubbo注解的解析流程
DubboComponetScan
@DubboComponentScan(basePackages = "com.gupaoedu.springboot.dubbo.springbootdubbosampleprovider.services") @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(DubboComponentScanRegistrar.class) public @interface DubboComponentScan { } // 这里面无非就是注册一个bean到 Spring IOC 里面 public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 这个就是获取我们在注解上 定义的 basePackages Set<String> packagesToScan = getPackagesToScan(importingClassMetadata); registerServiceAnnotationBeanPostProcessor(packagesToScan, registry); // @since 2.7.6 Register the common beans registerCommonBeans(registry); } --------------------------------------------------------------------------- private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) { // 在这里注册了 一个叫做 ServiceAnnotationBeanPostProcessor 的bean BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class); // 传递一个构造参数 packagesToScan 这里也就意味着服务的注册流程和 ServiceAnnotationBeanPostProcessor bean有关系 builder.addConstructorArgValue(packagesToScan); builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); // 将这个bean注册 BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry); }
ServiceAnnotationBeanPostProcessor
public ServiceAnnotationBeanPostProcessor(Set<String> packagesToScan) { super(packagesToScan); } // 进入super 因为实现了 BeanDefinitionRegistryPostProcessor,所以在bean装载完成之后,会触发postProcessBeanDefinitionRegistry 方法 public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware { public ServiceClassPostProcessor(Set<String> packagesToScan) { this.packagesToScan = packagesToScan; } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // 注册一个基础的bean registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, 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
这个bean会在spring 容器的上下文装载完成之后,触发监听
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) { // 上下文刷新的时候,也就是bean装载完成的时候 if (event instanceof ContextRefreshedEvent) { onContextRefreshedEvent((ContextRefreshedEvent) event); } else if (event instanceof ContextClosedEvent) { onContextClosedEvent((ContextClosedEvent) event); } }
registerServiceBeans
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) { // 定义一个scanner 用作扫描 DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader); // 生成一个bean的名字 BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry); scanner.setBeanNameGenerator(beanNameGenerator); // 为了兼容老的版本,实际上就是把需要扫描的注解类型,设置到Scanner。 // 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. // 查找@Service的所有beandefinitionholder // 相当于扫描所有加了 @DubboService注解的类 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 + "]"); } } } }
registerServiceBean
该bean和服务有关的信息,实际上都在我们刚刚定义的@DubboService
@DubboService( loadbalance = "random", cluster = "failover", retries = 2)
- 服务以什么协议发布
- 服务的负载均衡策略
- 服务的容错策略
- 服务发布端口
- …
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, DubboClassPathBeanDefinitionScanner scanner) { // 获取beanDefinitionHolder中的类对象 Class<?> beanClass = resolveClass(beanDefinitionHolder); // 在beanClass中查找@Service注解的存在 Annotation service = findServiceAnnotation(beanClass); /** * The {@link AnnotationAttributes} of @Service annotation */ //获取@Service注解的属性信息,包括interfaceClass等 AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false); // 根据@Service注解的属性信息,解析服务接口类 Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass); // 使用@Service注解的属性信息,接口类和注解的服务Bean名称构建服务Bean定义 String annotatedServiceBeanName = beanDefinitionHolder.getBeanName(); // 根据@Service注解的属性信息和接口类生成服务Bean的名称 AbstractBeanDefinition serviceBeanDefinition = buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName); // ServiceBean Bean name // 使用扫描器检查候选的Bean名称和服务Bean定义是否重复 String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass); if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean // 如果候选Bean通过检查,将服务Bean定义注册到Bean定义注册表中 // 通过 buildServiceBeanDefinition 得知这里面注册的就是ServiceBean 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?"); } } }
buildServiceBeanDefinition
private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation, AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass, String annotatedServiceBeanName) { // 通过大体阅读可以看到,其会将很多的配置信息构建到一个叫做 ServiceBean 里面 !!! BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(); String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol", "interface", "interfaceName", "parameters"); propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames)); // References "ref" property to annotated-@Service Bean addPropertyReference(builder, "ref", annotatedServiceBeanName); // Set interface builder.addPropertyValue("interface", interfaceClass.getName()); // Convert parameters into map builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters"))); // Add methods parameters List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods")); if (!methodConfigs.isEmpty()) { builder.addPropertyValue("methods", methodConfigs); } /** * Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference */ String providerConfigBeanName = serviceAnnotationAttributes.getString("provider"); if (StringUtils.hasText(providerConfigBeanName)) { addPropertyReference(builder, "provider", providerConfigBeanName); } /** * Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference */ String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor"); if (StringUtils.hasText(monitorConfigBeanName)) { addPropertyReference(builder, "monitor", monitorConfigBeanName); } /** * Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference */ String applicationConfigBeanName = serviceAnnotationAttributes.getString("application"); if (StringUtils.hasText(applicationConfigBeanName)) { addPropertyReference(builder, "application", applicationConfigBeanName); } /** * Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference */ String moduleConfigBeanName = serviceAnnotationAttributes.getString("module"); if (StringUtils.hasText(moduleConfigBeanName)) { addPropertyReference(builder, "module", moduleConfigBeanName); } /** * Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference */ String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry"); List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames); if (!registryRuntimeBeanReferences.isEmpty()) { builder.addPropertyValue("registries", registryRuntimeBeanReferences); } /** * Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference */ String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol"); List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames); if (!protocolRuntimeBeanReferences.isEmpty()) { builder.addPropertyValue("protocols", protocolRuntimeBeanReferences); } return builder.getBeanDefinition(); }
最终通过上述代码,讲一个 dubbo中提供的ServiceBean注入到Spring IOC容器
ServiceBean的初始化阶段
因为我们向spring注入了一个ServiceBean 那么在spring最后实例化阶段,即执行到 finishBeanFactoryInitialization 方法的时候就会调用到getBean方法从而通过反射去实例化,那么就会调用到ServiceBean 的构造方法。看看其构造函数
public ServiceBean() { super(); this.service = null; }
当ServiceBean初始化完成之后,会调用下面的方法.
@Override public void afterPropertiesSet() throws Exception { if (StringUtils.isEmpty(getPath())) { if (StringUtils.isNotEmpty(beanName) && StringUtils.isNotEmpty(getInterface()) && beanName.startsWith(getInterface())) { setPath(beanName); } } }
DubboBootstrapApplicationListener
在Dubbo中,DubboBootstrapApplicationListener是一个Spring应用程序监听器,它在Spring应用程序启动时会监听Dubbo的启动事件。
当启动 Dubbo服务时。
public class DubboBootstrapApplicationListener extends OnceApplicationContextEventListener 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(ApplicationContext applicationContext) { super(applicationContext); 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; } } // 监听的时候会进入到 onContextRefreshedEvent 里面
当开启start的时候可能会做的配置:
- 元数据/远程配置信息的初始化
- 拼接url()
- 如果是dubbo协议,则启动netty server
- 服务注册
start()
public DubboBootstrap start() { // 首先通过compareAndSet方法确保started标识为false,避免重复执行启动操作 if (started.compareAndSet(false, true)) { ready.set(false); // 调用initialize方法进行初始化。 initialize(); if (logger.isInfoEnabled()) { logger.info(NAME + " is starting..."); } // 1. export Dubbo Services // 导出Dubbo服务,即将服务暴露出去。 exportServices(); // Not only provider register // 如果不仅仅是注册提供者,并且已经导出了服务,那么还会导出MetadataService。 // 如果需要,注册本地ServiceInstance。 if (!isOnlyRegisterProvider() || hasExportedServices()) { // 2. export MetadataService exportMetadataService(); //3. Register the local ServiceInstance if required registerServiceInstance(); } referServices(); // 如果存在异步导出的服务,会启动一个新的线程来等待异步导出完成。 if (asyncExportingFutures.size() > 0) { new Thread(() -> { try { this.awaitFinish(); } catch (Exception e) { logger.warn(NAME + " exportAsync occurred an exception."); } // 最后设置ready标识为true,表示Dubbo框架已经准备就绪。 ready.set(true); if (logger.isInfoEnabled()) { logger.info(NAME + " is ready."); } }).start(); } else { ready.set(true); if (logger.isInfoEnabled()) { logger.info(NAME + " is ready."); } } if (logger.isInfoEnabled()) { logger.info(NAME + " has started."); } } return this; }
深入理解Dubbo-5.服务注册源码分析(中):https://developer.aliyun.com/article/1414061