• dubbo通过xml进行服务暴露源码解析


    在服务提供者进行服务暴露有两种方式:配置xml和引用注解这两种方式,这次讲解的是通过xml方式

    该过程主要有两步:解析xml创建bean和怎么根据bean的信息进行对应的暴露操作

    1、解析xml创建bean

    当我们配置了xml之后就了解怎么去解析配置的xml,在启动项目时,通过springboot框架监听相对应的事件就会去执行解析xml的操作。

    但DUBBO首先在DubboNamespaceHandler中注册自定义的xml解析器,在解析到xml中dubbo标签时就会去调用该解析器将每一项配置组装成ServiceBean对象

    这是<service:>标签的解析的注册语句

     

    据beanClass(ServiceBean)获取set方法,把传进来的标签element里的属性(如id、interface、class等)赋值给beanDefinition对象 

     

    这里beanDefinition的定义是记录着需要实例化bean的各种信息,相当于模子,有了模子就可以实例化相应的bean出来,返回的beanDefinition最终会放到spring一个beanDefinitionMap<String, BeanDefinition>中,其中key为xml定义的id

    在org.springframework.beans.factory.support.DefaultListableBeanFactory中当初次调用容器的getBean(beanName)时就会通过beanDefinitionMap获取BeanDefinition去实例化bean在这里将会去实例化ServiceBean实例,实例化的seriveBean归由spring容器管理,第一步就到此为至了。

    2、如何去暴露服务

    SeriveBean实现ApplicationListener<ContextRefreshedEvent>接口,在监听到容器触发相对应的事件调用onApplicationEvent()方法,然后调用父类的export()方法

    //判断是否是服务端服务,判断这个服务是否暴露以及通过ScheduledThreadPoolExecutor这个线程池类支持延迟暴露
    public synchronized void export() { if (provider != null) { if (export == null) { export = provider.getExport(); } if (delay == null) { delay = provider.getDelay(); } } if (export != null && !export) { return; } if (delay != null && delay > 0) { delayExportExecutor.schedule(new Runnable() { @Override public void run() { doExport(); } }, delay, TimeUnit.MILLISECONDS); } else { doExport(); } }

      接下来就是doExport()方法,在这个方法的前面就是做一些 check 操作,不是重点就不一一分析了。我们主要看一下它的appendProperties方法以及doExportUrls这两个方法。appendProperties()方法主要是为当前对象通过setter 方法来添加属性,它主要是通过以下方式来添加属性:

    • 从 System 中获取属性key值的优先通讯是: dubbo.provider. + 当前类 Id + 当前属性名称 > dubbo.provider. + 当前属性名称 为 key 获取值.
    • 首先从特定properties文件加载属性:首先 System.getProperty("dubbo.properties.file")获取到文件路径,如果获取不到就会试图加载 dubbo 的默认的路径 dubbo.properties加载。获取属性的 key 和上面从 System 里面获取的规则一样。

    然后我们就来分析一下doExportUrl这个方法,因为 Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。所以这里需要循环各个协议进行多协议暴露服务。

    private void doExportUrls() {
            List<URL> registryURLs = loadRegistries(true);
            for (ProtocolConfig protocolConfig : protocols) {
                doExportUrlsFor1Protocol(protocolConfig, registryURLs);
            }
        }
    接下来解析doExportUrlsFor1Protocol方法,该方法中appendParameters(Map, Object)的作用通过调用当前对象的getter方法获取到传入对象的值然后塞到 map 当中去。用于后面的构造URL这个对象。
    URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);

    接下来的执行就是服务的暴露,分为本地暴露和远程暴露

    本地暴露:本地暴露是暴露在JVM中,一个服务可能既是provider,又是consumer,自己调用自己的服务时不需要网络通信,每个服务默认都会在本地暴露,url是以injvm开头

    执行本地暴露的方法是exportLocal(url),进入方法中

     private void exportLocal(URL url) {
    //先判断,如果 URL 的协议头等于 injvm,说明已经导出到本地了,无需再次导出
    if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { URL local = URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(LOCALHOST) .setPort(0);
    //创建一个新的 URL 并将协议头、主机名以及端口设置成新的值 ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref)); Exporter
    <?> exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
    //proxyFactory.getInvoker()方法是生成一个Invoker对象,并调用 InjvmProtocol 的 export 方法导出服务。进入里面可以看到,在导出过程中利用exporterMap缓存了exporter exporters.add(exporter); logger.info(
    "Export dubbo service " + interfaceClass.getName() + " to local registry"); } }

    远程暴露:将IP,端口等信息暴露给远程客户端,调用时需要网络通信

    与导出服务到本地相比,导出服务到远程的过程要复杂不少,其包含了服务导出与服务注册两个过程

    //生成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);

    debug的时候可以看到protocol的实现类RegistryProtocol,进入export方法

     public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    //先进行服务导出,再进行服务暴露
    //export invoker final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);if (register) {
    //服务注册,它传递了两个参数,registryUrl(zookeeper注册中心地址)和registedProviderUrl(服务暴露地址) register(registryUrl, registedProviderUrl); ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(
    true); }
    ......
    }

    再debug进入doLocalExport()方法

    private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
            String key = getCacheKey(originInvoker);
            ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);//获取缓存的exporter对象
            if (exporter == null) {//判断,单例模式执行
                synchronized (bounds) {
                    exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
                    if (exporter == null) {
                        final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
                        exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);//这里的实现类是dubboProtocol,通过该类的export获取exporter
                        bounds.put(key, exporter);
                    }
                }
            }
            return exporter;
        }

    我们debug到dubboProtocol的export()方法中  public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {

            URL url = invoker.getUrl();
    
            // export service.
            String key = serviceKey(url);
            DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);//创建导出对象
            exporterMap.put(key, exporter);
    
            //export an stub service for dispatching event
           ........
    //dubbo底层是用netty的,创建server服务器,绑定本机网卡地址:通信端口,用于被调用服务的通信,具体的创建方法NettyServer#doOpen openServer(url); optimizeSerialization(url); return exporter; }

    我们可以返回服务注册了,创建zk客户端,创建对应节点,将服务元数据保存在zk上面。

    
    

    参考网址:

    https://blog.csdn.net/peace_hehe/article/details/79288053

    https://blog.csdn.net/aoomiao/article/details/83503223

  • 相关阅读:
    一起来开发Android的天气软件(三)——使用Volley实现网络通信
    Python 生成的页面中文乱码问题
    伴随着三维全息投影技术的升级,物理屏幕将彻底消失
    被忽视的TWaver功能(1)
    H2内存数据库 支持存储到文件
    LeetCode Merge Intervals
    (转)shiro权限框架详解06-shiro与web项目整合(下)
    (转) shiro权限框架详解06-shiro与web项目整合(上)
    (转)shiro权限框架详解05-shiro授权
    (转) shiro权限框架详解04-shiro认证
  • 原文地址:https://www.cnblogs.com/qizhufeitian/p/13943951.html
Copyright © 2020-2023  润新知