美文网首页
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