美文网首页
dubbo源码解析之暴露服务(二)

dubbo源码解析之暴露服务(二)

作者: binecy | 来源:发表于2019-03-19 20:43 被阅读0次

    源码分析基于dubbo 2.7.1

    从DubboNamespaceHandler可以看到,dubbo的标签都是由DubboBeanDefinitionParser解析。如

    registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
    

    需要关注一下DubboBeanDefinitionParser对service标签的处理

    RootBeanDefinition beanDefinition = new RootBeanDefinition();
    beanDefinition.setBeanClass(beanClass);
    beanDefinition.setLazyInit(false);
    String id = element.getAttribute("id");
    
    
    ...
    else if (ServiceBean.class.equals(beanClass)) {
        String className = element.getAttribute("class");
        if (className != null && className.length() > 0) {
            RootBeanDefinition classDefinition = new RootBeanDefinition();
            classDefinition.setBeanClass(ReflectUtils.forName(className));
            classDefinition.setLazyInit(false);
            parseProperties(element.getChildNodes(), classDefinition);
            beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
        }
    }
    

    beanClass就是DubboBeanDefinitionParser创建时的参数,这里就是ServiceBean.class。
    从上面代码可以看到,dubbo中的service都是ServiceBean, 如

    <dubbo:service id="dubboHelloService" interface="com.dubbo.start.service.HelloService" class="com.dubbo.start.service.HelloServiceImpl">
    

    则dubboHelloService解析为一个ServiceBean, ServiceBean中ref属性为HelloServiceImpl。(不要误以为dubboHelloService解析为HelloServiceImpl,这里和spring不一样)

    ServiceBean实现了ApplicationListener接口,spring启动时,会触发ServiceBean.onApplicationEvent方法,最终调用到父类ServiceConfig.doExport方法

        public synchronized void export() {
            // 检查和发布Config
            checkAndUpdateSubConfigs();
    
            if (!shouldExport()) {
                return;
            }
    
            if (shouldDelay()) {  // 延迟Export
                delayExportExecutor.schedule(this::doExport, delay, TimeUnit.MILLISECONDS);
            } else {
                doExport();
            }
        }
    

    doExport然后调用doExportUrls方法

        private void doExportUrls() {
            List<URL> registryURLs = loadRegistries(true);
            for (ProtocolConfig protocolConfig : protocols) {
                ...
                doExportUrlsFor1Protocol(protocolConfig, registryURLs);
            }
        }
    

    对每一种协议protocol,都要doExportUrlsFor1Protocol。
    我们一般使用dubbo protocol, <dubbo:protocol name="..." port="...">

    loadRegistries获取dubbo loadRegistries配置,并生成registry,格式为registry://...

    doExportUrlsFor1Protocol 花了很多代码将interface,method,timeout,validation这些参数放到一个map中,然后组成一个url

        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, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
            // 扩展
            if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .hasExtension(url.getProtocol())) {
                url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                        .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
            }
    
            String scope = url.getParameter(Constants.SCOPE_KEY);
            // scope为none不暴露服务
            if (!Constants.SCOPE_NONE.equalsIgnoreCase(scope)) {
                // 暴露本地服务
                if (!Constants.SCOPE_REMOTE.equalsIgnoreCase(scope)) {
                    exportLocal(url);
                }
                // 暴露远程服务
                if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) {
                    if (CollectionUtils.isNotEmpty(registryURLs)) {
                        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());
                            }
                            
                            // 获取代理配置
                            String proxy = url.getParameter(Constants.PROXY_KEY);
                            if (StringUtils.isNotEmpty(proxy)) {
                                registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
                            }
    
                            // 生成Invoker
                            Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                            DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                            // <服务注册>
                            Exporter<?> exporter = protocol.export(wrapperInvoker);
                            exporters.add(exporter);
                        }
                    } else {
                        ...
                    }
                    ...
                }
            }
            this.urls.add(url);
        }
    

    暴露本地接口使用InjvmProtocol处理。

    本文只关注远程接口的暴露。

    registryURLs的格式为registry://..., 所以<服务注册> 会调用RegistryProtocol.export。

    dubbo zookeeper的注册会在下一篇文章讲解。

    相关文章

      网友评论

          本文标题:dubbo源码解析之暴露服务(二)

          本文链接:https://www.haomeiwen.com/subject/ozpbmqtx.html