Dubbo最核心功能——服务暴露的配置、使用及原理

慈云数据 7个月前 (04-27) 技术支持 66 0

系列文章目录

【收藏向】从用法到源码,一篇文章让你精通Dubbo的SPI机制

面试Dubbo ,却问我和Springcloud有什么区别?

超简单,手把手教你搭建Dubbo工程(内附源码)


文章目录

  • 系列文章目录
  • 前言
  • 一、服务暴露的分类
  • 二、本地暴露
    • 1. 配置
    • 2. 实现原理
    • 三、远程暴露
      • 1. 配置
      • 2. 实现原理
      • 四、服务的注解配置
      • 五、服务暴露的触发
        • 1. dubbo 的启动
        • 2. 服务识别——后置处理器
        • 3. 服务暴露的时机
        • 4. 与Spring结合的流程图

          前言

          今天开始,将正式的进入Dubbo 核心功能的掌握与学习,Dubbo的最核心功能是什么?自然是其RPC功能,而该功能其实分为三个方面:

          1.服务暴露

          2.服务引用

          3.服务调用

          三者分别专注于服务提供者、服务消费者、服务调用过程。我们今天就先关注服务暴露,我们至少有这么几个疑问待解决:

          • 服务暴露有几种,分别是怎么实现的
          • 我们如何进行并配置服务暴露
          • Dubbo和Spirng框架是怎么结合的

            📕作者简介:战斧,多年开发及管理经验,爱好广泛,致力于创作更多高质量内容

            📗本文收录于 Dubbo专栏,有需要者,可直接订阅专栏实时获取更新

            📘高质量专栏 RabbitMQ、Spring全家桶 等仍在更新,欢迎指导

            📙Zookeeper Redis kafka docker netty等诸多框架,以及架构与分布式专题即将上线,敬请期待


            一、服务暴露的分类

            看过我们之前的文章 超简单,手把手教你搭建Dubbo工程(内附源码) 的读者,应该有所了解了。即Dubbo 实际上有两种服务暴露的类型,即远程暴露和本地暴露

            1. 远程暴露

              远程暴露是指将服务发布到远程注册中心,供消费者调用。Dubbo支持多种远程通信协议,例如dubbo、rmi、hessian、http等。远程暴露的实现原理大致如下:

              服务提供者在启动时,将服务实现类通过协议进行网络传输,发布到注册中心上。

              服务消费者在启动时,从注册中心上获取到服务提供者的地址,然后通过协议进行网络通信,调用服务提供者的方法。

              在这里插入图片描述

            2. 本地暴露

              本地暴露是指将服务发布到本地JVM上,供同一JVM中的其他服务调用。Dubbo的本地通信使用了JVM内部的IPC或者共享内存等方式,从而避免了网络传输的开销。本地暴露的实现原理大致如下:

              服务提供者在启动时,将服务实现类通过代理方式,注入到本地JVM的缓存中。

              服务消费者在启动时,直接从本地JVM缓存中获取到服务提供者的代理对象,然后调用服务提供者的方法。

              在这里插入图片描述

            PS:需要注意的是,本地暴露仅限于同一JVM内的服务调用,如果需要跨进程或跨机器进行通信,仍需要使用远程通信方式

            二、本地暴露

            有的人会问,Dubbo提供本地调用的服务是什么意思?因为大部分人都是把Dubbo当作RPC框架使用的,为什么其还会提供本地暴露这种用法?

            应该说:Dubbo的本地暴露实际上是比较少用到的,但对于一些需要高性能、低延迟、本地调用的应用场景,使用本地暴露可以提高服务的性能和可用性。从实际项目上来讲,笔者接触过一些项目,其功能全部实现了对外暴露,也就是说有很多功能,既允许外部远程调用,也能被本地其他方法调用。这个时候,本地的调用如果还走RPC的路子,不仅带来延时,还无谓地浪费了带宽,支持本地调用就很有必要了

            1. 配置

            在Dubbo 2.2 0版本开始,Dubbo默认在本地以JVM的方式暴露服务,当然,对于老版本而言,还需要进行一些配置。这样的话,在第一步需要在整体配置文件里开启

            #开启服务暴露
            dubbo.provider.export= true
            

            其次就是在单个接口上,我们需要配置引用的源scope = local

            
            
            
            

            2. 实现原理

            我们来看进行服务暴露(或者说导出)的基础方法 ServiceConfig 下私有方法 exportUrl

            private void exportUrl(URL url, List registryURLs) {
                String scope = url.getParameter(SCOPE_KEY);
                // 如果配置scope为"none" ,就不进行服务暴露
                if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
                    // 除非显式的配置了远程暴露,不然都会进行本地暴露
                    if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
                        exportLocal(url);
                    }
                    // 除非显式的指定了本地暴露,不然都会进行远程暴露
                    if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
                       // 远程导出省略.....
                    }
                }
                // 不难看出,`scope` 字段采取的判断逻辑非常宽容,试想一下,如果填入 scope = "abc" 会发生什么?
                this.urls.add(url);
            }
            

            通过上述方法,我们知道了除非指定某接口就是远程暴露,否则都会进行本地暴露。那具体本地暴露是怎么回事呢?我们继续来看

            private Protocol protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension();
            String LOCAL_PROTOCOL = "injvm";
            String LOCALHOST_VALUE = "127.0.0.1";
            private void exportLocal(URL url) {
                URL local = URLBuilder.from(url)
                    .setProtocol(LOCAL_PROTOCOL)
                    .setHost(LOCALHOST_VALUE)
                    .setPort(0)
                    .build();
                local = local.setScopeModel(getScopeModel())
                    .setServiceModel(providerModel);
                doExportUrl(local, false);
                logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
            }
            private void doExportUrl(URL url, boolean withMetaData) {
                Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                if (withMetaData) {
                    invoker = new DelegateProviderMetaDataInvoker(invoker, this);
                }
                Exporter exporter = protocolSPI.export(invoker);
                exporters.add(exporter);
            }
            

            不难发现,本地暴露并没有采用取巧的方式去做特殊处理,而是老老实实构建了个URL,并且像模像样的进行导出,只是这个URL的设置了协议类型为injvm,主机更是直接127.0.0.1。当然最重要的是 protocolSPI.export(invoker) 这个代码,这里的 protocolSPI 是利用的Dubbo-SPI的自适应机制,所以导致最终执行导出的实现类为 InjvmProtocol (关于Dubbo-SPI的内容可见 一篇文章让你精通Dubbo的SPI机制),我们来看 InjvmProtocol 的 export 方法

            // AbstractProtocol.class
            protected final Map> exporterMap;
                InjvmExporter(Invoker invoker, String key, Map
微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon