Dubbo源碼解析 三、Provider的啓動過程

  上一節中講到了dubbo中consumer的啓動過程,鏈接爲 Dubbo源碼解析 二、Consumer的啓動過程 ,這一節我們將一些provider的啓動過程,看一下dubbo是如何將provider提供的服務暴露出來。

  同樣的,下面以provider中 DemoServiceImpl 爲例:

public class DemoServiceImpl implements DemoService {

    @Override
    public String sayHello(String name) {
        System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
    }

}
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- provider's application name, used for tracing dependency relationship -->
    <dubbo:application name="demo-provider"/>

    <!-- use multicast registry center to export service -->
    <!--<dubbo:registry address="multicast://224.5.6.7:1234"/>-->
    <dubbo:registry group="providertest" protocol="zookeeper" address="127.0.0.1:2181" />

    <!-- use dubbo protocol to export service on port 20880 -->
    <dubbo:protocol name="dubbo" port="20880"/>

    <!-- service implementation, as same as regular local bean -->
    <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>

    <!-- declare the service interface to be exported -->
    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>

</beans>

  首先就是要找到其<dubbo:service>的beanDefinitionParse,找到其最終的實現類:

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
    public void init() {
	. . . 
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
	. . .
    }

}

  dubbo中的解析器DubboBeanDefinitionParser在上一節中已經講到到,這一篇就不再贅述了。我們可以看到最終根據這個節點生成的是ServiceBean,我們看一下ServierBean繼承類與實現的接口:

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
	. . . 
}

 可以看到,ServiceBean實現了Spring中的DisposableBean與ApplicationListener<ContextRefreshedEvent>接口,說明ServiceBean實現了自定義的初始化方法,且監聽了ContextRefreshedEvent事件,也就是說在Spring容器啓動後的事件,我們分別看一下這些方法中做了哪些處理:

   @Override
    @SuppressWarnings({"unchecked", "deprecation"})
    public void afterPropertiesSet() throws Exception {

	// 獲取ProtocolConfig、ApplicationConfig ModuleConfig.class、RegistryConfig等信息,這點與ReferenceBean的afterPropertiesSet()方法很像,就不再贅述
        if (getProvider() == null) {
            . . .
        }
        if (getApplication() == null
                && (getProvider() == null || getProvider().getApplication() == null)) {
            . . .
        }
        if (getModule() == null
                && (getProvider() == null || getProvider().getModule() == null)) {
            . . .
        }
        if ((getRegistries() == null || getRegistries().isEmpty())
                && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().isEmpty())
                && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().isEmpty())) {
	    . . .
        }
	. . .
       	// 是否延遲導出,如果不是,在初始化bean的時候即導出服務
        if (!isDelay()) {
            export();
        }
    }

  而在ContextRefreshedEvent事件中的處理爲:

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (isDelay() && !isExported() && !isUnexported()) {
            if (logger.isInfoEnabled()) {
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            export();
        }
    }

因爲Service一定是要導出才能對外提供服務,如果如果要延遲加導出的話,那麼導出動作是在Spring容器啓動完成之後進行的,我們看一下導出做了哪些事情:

   protected synchronized void doExport() {
      
	. . .
	// 檢查相應組件
        checkApplication();
        checkRegistry();
        checkProtocol();
        appendProperties(this);
        checkStubAndMock(interfaceClass);
        if (path == null || path.length() == 0) {
            path = interfaceName;
        }
	// 將服務進行導出
        doExportUrls();
        ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
        ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
    }

    private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);
	// 每個協議配置都需要單獨導出一次,
        for (ProtocolConfig protocolConfig : protocols) {
	    // 每個協議配置都需要在不同的註冊中心進行註冊
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }


    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        . . .


        String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
        Integer port = this.findConfigedPorts(protocolConfig, name, map);
        URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);


        String scope = url.getParameter(Constants.SCOPE_KEY);
        // don't export when none is configured
        if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {

            // export to local if the config is not remote (export to remote only when config is remote)
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                exportLocal(url);
            }
            // export to remote if the config is not local (export to local only when config is local)
            if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (registryURLs != null && !registryURLs.isEmpty()) {
                    for (URL registryURL : registryURLs) {
                        url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
                        URL monitorUrl = loadMonitor(registryURL);
                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }

                        // 注意,此invoker不是通過protocol來獲取到的,而是通過proxyFactory,直接得到一個本地ref的代理對象invoker,調用此invoker的方法即最終會調用ref的方法
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
			// 將服務進行導出,假如我們的註冊中心是zooKeeper,那麼會將服務註冊早zooKeeper上面
                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
            }
        }
        this.urls.add(url);
    }



至此,服務導出完畢

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章