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相关的东西已经走完了,下面做一个总结
- 通过Spring Boot的自动装配或@EnableDubbo注解自动注入一个ServiceAnnotationBeanPostProcessor传入需要扫描的包的路径
- 注册了一个``DubboBootstrapApplicationListener`监听
- 根据BeanPostProcessor的特性,调用postProcessBeanDefinitionRegistry方法,根据传入的扫包路径进行扫描,然后将所有的标注了@Service注解的bean注入到ioc容器中
- 继续扫描包,获得标注了Service/DubboService等注解的所有BeanDefinitionHolders
- 遍历BeanDefinitionHolders,解析出每个BeanDefinition中的接口,标注的注解,及注解上定义的参数等。
- 通过解析出来的一系列信息生成一个ServiceBean.然后将ServiceBean注入到ioc容器中。
- 同时在ServiceBean的父类AbstractConfig中,会存在一个标注了@PostConstruct注解的方法,它会在bean初始化完成之后,将当前bean保存到一个Co
以上是关于Dubbo系列讲解之服务发现3万字长文分享的主要内容,如果未能解决你的问题,请参考以下文章
万字长文Dubbo 入门总结 ,一款高性能的 Java RPC 框架