文章目錄
Dubbo配置解析原理
關注可以查看更多粉絲專享blog~
上次本地編譯的最新的Dubbo 2.7.8,跟着書看源碼是基於2.6.5,從2.7.0版本開始dubbo的groupId已經變了。這裏查看2.6.5的源碼有兩種方案
- 【推薦】下載Dubbo 2.6.5源碼編譯,編譯流程和2.7.8一樣。
- 直接用maven引入了alibaba dubbo 2.6.5的包,用IDEA查看源碼學習,有個坑就是ali在dubbo裏面有用自己的Spring support,需要單獨依賴,不然有些類是沒有的。
<!-- 2.7.0之前 -->
<groupId>com.alibaba</groupId>
<!-- 2.7.0開始 -->
<groupId>org.apache.dubbo</groupId>
<!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo -->
<!-- 2.7.0開始groupId變更爲org.apache.dubbo -->
<!--<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.0</version>
</dependency>-->
<!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba.spring/spring-context-support -->
<dependency>
<groupId>com.alibaba.spring</groupId>
<artifactId>spring-context-support</artifactId>
<version>1.0.6</version>
</dependency>
基於XML配置解析原理
什麼是XSD?Dubbo框架直接集成了Spring的能力,利用了Spring配置文件擴展出自定義的解析方式dubbo.xsd。
dubbo.xsd文件用來約束使用XML配置時的標籤和對應的屬性,比如Dubbo中的<dubbo:service>和<dubbo:reference>標籤等。Spring在解析到自定義的namespace標籤時,會查找對應的spring。schemas和spring.handlers文件,最終觸發Dubbo的spaceHandler類來進行初始化和解析。
schema模塊說明
類型定義 | 功能概述 |
---|---|
applicationType | 配置應用級別的信息,比如應用名稱,應用負責人和應用版本等 |
protocolType | 配置服務提供者暴露的協議,Dubbo允許同時配置多個協議,但只能有一個協議默認暴露 |
registryType | 配置註冊中心的地址和協議,Dubbo也允許多個註冊中心同時使用 |
providerType | 配置服務提供方的全局配置,比如服務方設置了timeout,消費方會自動透傳超時 |
consumerType | 配置消費方全局配置,比如connections屬性代表客戶端會創建的TCP連接數,客戶端全局配置會覆蓋providerType透傳的屬性 |
serviceType | 配置服務提供方接口範圍信息,比如服務暴露的接口和具體實現類等 |
referenceType | 配置消費方接口範圍信息,比如引入的接口名稱和是否範化調用標誌等 |
moduleType | 配置應用所屬模塊信息 |
monitorType | 配置應用監控上報相關地址 |
methodType | 配置方法級別參數,主要應用於<dubbo:service>和<dubbo:reference> |
argumentType | 配置應用參數方法等輔助信息,比如高級特性中異步參數回調索引的配置等 |
parameterType | 選項參數配置,可以作爲<dubbo:protocol>、<dubbo:service>、<dubbo:reference>、<dubbo:provider>和<dubbo:consumer>的字標籤,方便添加自定義參數,會透傳到框架的URL中 |
有了dubbo.xsd中約束的定義,以及如何擴展字段之後主要的解析邏輯在DubboBeanDefinitionParser#parse中完成。主要內容包含將標籤解析成對應的Bean定義並註冊到Spring上下文中,同時保證Spring容器中相同id的Bean不會覆蓋。
// public class DubboNamespaceHandler extends NamespaceHandlerSupport implements ConfigurableSourceBeanMetadataElement
@Override
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.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());
}
基於註解配置解析原理
註解處理邏輯主要包含3部分內容:
- 如果用戶使用了配置文件,則框架按需生成對應的Bean。
- 要將所有使用Dubbo註解@Service的class提升爲Bean。
- 要爲使用@Reference註解的字段或方法注入代理對象。
@EnableDubbo
// @EnableDubbo 註解上加了@EnableDubboConfig、@DubboComponentScan
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
...
}
// @EnableDubboConfig 註解上import了DubboConfigConfigurationSelector類
@Import(DubboConfigConfigurationSelector.class)
public @interface EnableDubboConfig {
...
}
// @DubboComponentScan 註解上import了DubboComponentScanRegistrar類
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
...
}
@DubboComponentScan
- 激活DubboComponentScanRegistrar
- 生成ServiceAnnotationBeanPostProcessor處理器
- 生成ReferenceAnnotationBeanPostProcessor處理器。
// public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar
// DubboComponentScanRegistrar#registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 獲取掃描包路徑
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
// 激活ServiceAnnotationBeanPostProcessor
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
// 激活ReferenceAnnotationBeanPostProcessor
registerReferenceAnnotationBeanPostProcessor(registry);
}
ServiceAnnotationBeanPostProcessor的作用
// ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 獲取用戶註解配置的包掃描(@EnableDubbo 的value值,沒有則默認註解類的包路徑)
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
// 觸發ServiceBean的定義和注入
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// 掃描Dubbo的註解@Service,不會掃描Spring的@Service註解
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
for (String packageToScan : packagesToScan) {
// 將@Service作爲不同的Bean注入容器
scanner.scan(packageToScan);
// 對掃描的服務創建Beandefinitionholder,用於生成ServiceBean定義
Set<BeanDefinitionHolder> beanDefinitionHolders = findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 註冊ServiceBean定義與數據綁定
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
...
}
}
}
ReferenceAnnotationBeanPostProcessor的作用
public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor<Reference>
implements ApplicationContextAware, ApplicationListener
// 父類 AnnotationInjectedBeanPostProcessor#postProcessPropertyValues
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
// 查找Bean所有標註了@Reference的字段和方法
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
// 對字段、方法進行反射綁定
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getName()
+ " dependencies is failed", ex);
}
return pvs;
}
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// 返回到類名作爲緩存鍵,以便向後兼容自定義調用者
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// 首先對併發映射進行快速檢查,使用最小的鎖定
AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
try {
// 處理註解字段和方法
metadata = buildAnnotatedMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
} catch (NoClassDefFoundError err) {
throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
"] for annotation metadata: could not find class that it depends on", err);
}
}
}
}
return metadata;
}
// 處理帶@Reference的字段和方法
private AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
// 處理帶@Reference的字段
Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
// 處理帶@Reference的方法
Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
// 注入容器
return new AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
// 處理帶@Reference的字段
private List<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {
final List<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement>();
ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
// 遍歷查找所有帶@Reference的非靜態字段添加到List最後返回
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
A annotation = getAnnotation(field, getAnnotationType());
if (annotation != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@" + getAnnotationType().getName() + " is not supported on static fields: " + field);
}
return;
}
elements.add(new AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement(field, annotation));
}
}
});
return elements;
}
// 處理帶@Reference的方法
private List<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> findAnnotatedMethodMetadata(final Class<?> beanClass) {
final List<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> elements = new LinkedList<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement>();
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
// 遍歷查找所有帶@Reference的非靜態方法添加到List最後返回
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Method bridgedMethod = findBridgedMethod(method);
if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
A annotation = findAnnotation(bridgedMethod, getAnnotationType());
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@" + getAnnotationType().getSimpleName() + " annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
logger.warn("@" + getAnnotationType().getSimpleName() + " annotation should only be used on methods with parameters: " +
method);
}
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
elements.add(new AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement(method, pd, annotation));
}
}
});
return elements;
}
配置初始化
不管是在服務暴露還是在服務消費場景下,Dubbo框架都會根據優先級對配置信息做聚合處理,目前默認覆蓋策略主要遵循以下三點規則:
- -D傳遞給JVM參數優先級最高,比如-Ddubbo.protocol.port=20880
- 代碼或者XML配置優先級次高,如:Spring中XML文件制定<dubbo:protocol=“20880”>
- 配置文件優先級最低,如:dubbo.properties文件指定dubbo.protocol.port=20880
一般推薦使用dubbo.properties作爲默認值,只有JVM沒有指定參數,並且XML沒有匹配時,dubbo.properties纔會生效,通常用語共享公共配置,如應用名稱等。
Dubbo的配置也會受到provider的影響,這個屬於運行期屬性值影響,同樣遵循一下兩點規則:
- 如果只有provider端指定配置,則會自動透傳到客戶端(如:timeout)
- 如果客戶端也有相應配置,則服務端配置會被覆蓋(如:timeout)
運行時屬性隨着框架特性可以動態添加,不允許透傳的屬性會在ClusterUtils#mergeUrl中進行特殊處理。
// ClusterUtils#mergeUrl
public static URL mergeUrl(URL remoteUrl, Map<String, String> localMap) {
Map<String, String> map = new HashMap<String, String>();
Map<String, String> remoteMap = remoteUrl.getParameters();
if (remoteMap != null && remoteMap.size() > 0) {
map.putAll(remoteMap);
// 移除禁止透傳的參數
map.remove(Constants.THREAD_NAME_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREAD_NAME_KEY);
map.remove(Constants.THREADPOOL_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREADPOOL_KEY);
map.remove(Constants.CORE_THREADS_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.CORE_THREADS_KEY);
map.remove(Constants.THREADS_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREADS_KEY);
map.remove(Constants.QUEUES_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.QUEUES_KEY);
map.remove(Constants.ALIVE_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.ALIVE_KEY);
map.remove(Constants.TRANSPORTER_KEY);
map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.TRANSPORTER_KEY);
}
if (localMap != null && localMap.size() > 0) {
map.putAll(localMap);
}
if (remoteMap != null && remoteMap.size() > 0) {
// 使用從provider 傳遞的version
String dubbo = remoteMap.get(Constants.DUBBO_VERSION_KEY);
if (dubbo != null && dubbo.length() > 0) {
map.put(Constants.DUBBO_VERSION_KEY, dubbo);
}
String version = remoteMap.get(Constants.VERSION_KEY);
if (version != null && version.length() > 0) {
map.put(Constants.VERSION_KEY, version);
}
String group = remoteMap.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
map.put(Constants.GROUP_KEY, group);
}
String methods = remoteMap.get(Constants.METHODS_KEY);
if (methods != null && methods.length() > 0) {
map.put(Constants.METHODS_KEY, methods);
}
// 保留provider url的時間戳
String remoteTimestamp = remoteMap.get(Constants.TIMESTAMP_KEY);
if (remoteTimestamp != null && remoteTimestamp.length() > 0) {
map.put(Constants.REMOTE_TIMESTAMP_KEY, remoteMap.get(Constants.TIMESTAMP_KEY));
}
// 在Provider和Consumer上組合過濾器和偵聽器
String remoteFilter = remoteMap.get(Constants.REFERENCE_FILTER_KEY);
String localFilter = localMap.get(Constants.REFERENCE_FILTER_KEY);
if (remoteFilter != null && remoteFilter.length() > 0
&& localFilter != null && localFilter.length() > 0) {
localMap.put(Constants.REFERENCE_FILTER_KEY, remoteFilter + "," + localFilter);
}
String remoteListener = remoteMap.get(Constants.INVOKER_LISTENER_KEY);
String localListener = localMap.get(Constants.INVOKER_LISTENER_KEY);
if (remoteListener != null && remoteListener.length() > 0
&& localListener != null && localListener.length() > 0) {
localMap.put(Constants.INVOKER_LISTENER_KEY, remoteListener + "," + localListener);
}
}
return remoteUrl.clearParameters().addParameters(map);
}
相關文章:
Davids原理探究:Dubbo源碼編譯(2.7.8)
Davids原理探究:Dubbo SPI和Java SPI實現原理
Davids原理探究:Dubbo註冊中心(ZooKeeper、Redis)實現原理
Davids原理探究:Dubbo配置解析原理
Davids原理探究:Dubbo服務暴露原理
Davids原理探究:Dubbo服務消費原理
Davids原理探究:Dubbo優雅停機原理解析
Davids原理探究:Dubbo調用流程圖
Davids原理探究:Dubbo路由實現原理
Davids原理探究:Dubbo負載均衡實現原理
Davids原理探究:Dubbo過濾器原理