天天看點

(三)DUBBO源碼解析[email protected] provider釋出服務過程

先祭概述

本文将從 spring如何加載執行個體入手:

  1. 先分析如何加載spring普通執行個體
  2. 再分析如何加載 dubbo 服務
  3. 然後分析加載 dubbo 服務後,如何釋出服務

本文分析的dubbo 服務注冊中心是 zookeeper,其它注冊中心(如redis)應該差别不大。 本文面向的讀者,首先應該會用 springboot,并且會用 dubbo,但是對他們的源代碼或者原理不是很清楚。

再祭版本

springboot 版本 2.x

spring 版本5.x

dubbo 版本 2.6.0

OK , let’s begin!!

HouseDao 是一個普通的 spring 服務, 其@service 注解來自 spring

import org.springframework.stereotype.Service;

@Service
public class HouseDao {

    public void build(){

        System.out.println("house build");
    }

}
           

HelloServiceImpl 是一個 dubbo provider, 其@Service 注解來自 dubbo

import com.alibaba.dubbo.config.annotation.Service;

@Service
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String word) {


        return "hello " + word;
    }
}
           

part 1: 我們首先來看普通服務HouseDao(spring的 @Service)是如何加載的

在構造方法中打一個斷點:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

找到初始化堆棧:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

我們依次點進去,發現初始化入口在DefaultListableBeanFactory的 preInstantiateSingletons()方法中:

public void preInstantiateSingletons() throws BeansException {
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Pre-instantiating singletons in " + this);
		}


		//所有需要執行個體化的服務放在清單中
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
//省略大量代碼.....
				}
				else {
//本行代碼執行個體化bean
					getBean(beanName);
				}
			}
		}
		// ...
	}
           

是以我們需要搞清楚this.beanDefinitionNames 是何時被指派的,找到這個beanDefinitionNames的聲明處,然後找到他所有的引用:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

如上圖,發現這個beanDefinitionNames變量引用雖然很多,但是寫入的引用隻有兩處,其中一出還是删除, 那很顯然, 複制就是在另外一處啦,也就是registerBeanDefinition()方法。

@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		//省略代碼。。。
		BeanDefinition oldBeanDefinition;
		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		if (oldBeanDefinition != null) {
			//省略大量代碼。。。。
		}
		else {
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					//省略。。。

					// beanName 是方法參數
					updatedDefinitions.add(beanName); 
					//beanDefinitionNames 是在此處指派
					this.beanDefinitionNames = updatedDefinitions; 

					//省略。。。
				}
			}
			else {
				//省略代碼。。。
			}
			this.frozenBeanDefinitionNames = null;
		}
		//省略代碼。。。。
	}

           

可以看到關鍵參數 beanName 是有上遊方法傳過來的。OK ,那麼在 這一行加一個有條件的斷點,當 beanName 為 houseDao的時候斷住,看看這時候上遊代碼發生了什麼。

(三)DUBBO源碼解析[email protected] provider釋出服務過程

執行到斷點位置時,我們打開調用棧,依次觀察下去,會發現兩個重要的位置,就是下面圈紅的位置:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

第二個紅框代碼是:

// Invoke factory processors registered as beans in the context.
	invokeBeanFactoryPostProcessors(beanFactory);

           

第一個紅框代碼是:

總體看來就是調用 BeanFactoryPostProcessor. 如果對 spring bean 生命周期有些概念,其實不 debug 代碼,我們也會猜到, @Service注解一定是通過BeanFactoryPostProcessor處理的,BeanFactoryPostProcessor這個接口就是用來管理bean 的定義的(BeanDefinition) , 那些需要執行個體化到容器中的 bean定義,一定是執行個體化之前先加載好的, 而在執行個體化之前能做這個事情的,應該隻有BeanFactoryPostProcessor。 但是 debug 代碼後我們會知道具體是那個BeanFactoryPostProcessor處理的,也就是ConfigurationClassPostProcessor的 postProcessBeanDefinitionRegistry()方法。 具體這個方法是怎麼做找到要加載的類就不再具體分析了,大緻看下,應該是掃描包路徑,識别注解類型。

part 2: 接下來我們分析 dubbo provider HelloServiceImpl是如何執行個體化的

spring javabean 是什麼時候執行個體化的本文的上半部分已經讨論過了,至于那些 bean 會被執行個體則依賴于DefaultListableBeanFactory類的 beanDefinitionNames屬性。

是以我們首先讨論下 HelloServiceImpl 這個 dubbo server 的名稱是什麼時候被加入beanDefinitionNames的。根據本文上半部分,大概猜到dubbo的@Service注解應該也是通過某個 BeanFactoryPostProcessor處理的。 類似的在DefaultListableBeanFactory registerBeanDefinition方法打個有條件的斷點:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

然後觀察調用堆棧:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

很容易發現是ServiceAnnotationBeanPostProcessor這個類在處理dubbo 的@Service 接口。這個類也是一個 BeanFactoryPostProcessor的派生類,并且是 dubbo(com.alibaba.dubbo.config.spring.beans.factory.annotation) 包提供的。 因為我用@DubboComponentScan注解定義了 dubbo Server的掃描路徑,是以從調用棧可以看到這個processor大緻處理過程是掃描包路徑,從 classPath 中尋找中繼資料,這裡就不做過多分析了。

至此我們已經大概 dubbo server是什麼時候由誰執行個體化的了,接下來我們分析下,dubbo server是什麼時候向 zookeeper釋出的。

part 3: dubbo provider 釋出過程

先看下dubbo 的官方架構圖:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

從架構圖上看,貌似是 ServiceConfig類的 export 方法執行的服務釋出。我們在dubbo 源代碼中實際加斷點測試,也确實是這個方法做的釋出(最終的釋出代碼是ServiceConfig doExportUrlsFor1Protocol()方法中的Exporter<?> exporter = protocol.export(wrapperInvoker);)。

OK, 接下來我們在 export方法中加個斷點,看下調用堆棧:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

可以看到釋出服務的動作發生在applicationContext refresh完成的末尾:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

在這個方法裡,applicationContext 釋出了一個事件:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

dubbo 的 ServiceBean會監聽這個事件:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

ServiceBean繼承自ServiceConfig, 這裡的 export()就是 ServiceConfig 的 export()。而此時的ServiceConfig已經持有了具體的服務對象:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

export()放回會根據 ServiceBean 持有的對象以及其它上下文來執行釋出動作。

    我們可以看出,其實 dubbo provider 會被封裝成一個 ServiceBean, 而 ServiceBean 會在applicationContext即将 refresh完成的時候,監聽時間負責釋出任務。是以接下來的關鍵,是看ServiceBean如何執行個體化的。

part 3: ServiceBean執行個體化過程

    ServiceBean的執行個體化代碼與上兩部分相同,同樣依賴于DefaultListableBeanFactory類的 beanDefinitionNames屬性。關鍵點依然在于ServiceBean name 是什麼時候加入到beanDefinitionNames屬性中的。

    還是在同樣的地方加類似的斷點:

(三)DUBBO源碼解析[email protected] provider釋出服務過程

檢視調用棧,發現也是ServiceAnnotationBeanPostProcessor注冊的 bean. 具體方法如下:

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

     //省略代碼

        for (String packageToScan : packagesToScan) {
            Set<BeanDefinitionHolder> beanDefinitionHolders = scanner.doScan(packageToScan);
            if (CollectionUtils.isEmpty(beanDefinitionHolders)) {
               //省略
                beanDefinitionHolders = findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
            }
            
            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                //此處是注冊 serviceBean 的代碼。
                    registerServiceBean(beanDefinitionHolder, registry);
                }
            	 //省略日志的代碼
            } 
            //省略日志的代碼
        }
    }
           

可以看到注冊 serviceBean 主要是依賴 beanDefinitionHolder這個參數,

這個參數是如何得到呢?就在上面幾行:

有心的同學發現,這個行代碼正是注冊 helloServiceImpl這個 dubbo provider 的 bean的 beanDefinition所執行的代碼。

也就是說ServiceAnnotationBeanPostProcessor會先注冊 dubbo provider的 BeanDefinition, 然後會傳回這個BeanDefinition的BeanDefinitionHolder, 然後根據BeanDefinitionHolder注冊這個 dubbo provider所依賴的 ServiceBean

至此,就完成了 dubbo provider的執行個體化和釋出過程,通過閱讀這段代碼,會加深對 BeanFactoryPostProcessor 的了解,也會更加了解BeanFactoryPostProcessor和 BeanDefinition 之間是如何工作的。

============================================================

ServiceAnnotationBeanPostProcessor 這是一個 BeanFactoryPostProcessor的派生類,并且是 dubbo(com.alibaba.dubbo.config.spring.beans.factory.annotation) 包提供的, 負責管理 BeanDefinition

104 行 注冊 dubbo service , 并傳回beanDefinitionHolders

121 行 通過beanDefinitionHolders 注冊 dubbo ServiceBean

繼續閱讀