1. 程式人生 > >Dubbo原理和原始碼解析之服務暴露

Dubbo原理和原始碼解析之服務暴露

github新增倉庫 "dubbo-read"(點此檢視),集合所有《Dubbo原理和原始碼解析》系列文章,後續將繼續補充該系列,同時將針對Dubbo所做的功能擴充套件也進行分享。不定期更新,歡迎Follow。

一、框架設計

在官方Dubbo 使用者指南架構部分,給出了服務呼叫的整體架構和流程:

另外,在官方《Dubbo 開發指南》框架設計部分,給出了整體設計:

以及暴露服務時序圖:

本文將根據以上幾張圖,分析服務暴露的實現原理,並進行詳細的程式碼跟蹤與解析。

二、原理和原始碼解析

2.1 標籤解析

從文章Dubbo原理和原始碼解析之標籤解析

中我們知道,<dubbo:service> 標籤會被解析成 ServiceBean

ServiceBean 實現了 InitializingBean,在類載入完成之後會呼叫 afterPropertiesSet() 方法。在 afterPropertiesSet() 方法中,依次解析以下標籤資訊:

  • <dubbo:provider>
  • <dubbo:application>
  • <dubbo:module>
  • <dubbo:registry>
  • <dubbo:monitor>
  • <dubbo:protocol>

ServiceBean 還實現了 ApplicationListener,在 Spring 容器初始化的時候會呼叫 onApplicationEvent 方法。ServiceBean 重寫了 onApplicationEvent 方法,實現了服務暴露的功能。

ServiceBean.java

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

2.2 延遲暴露

ServiceBean 擴充套件了 ServiceConfig,呼叫 export() 方法時由 ServiceConfig 完成服務暴露的功能實現。

ServiceConfig.java

public synchronized void export() {
    if (provider != null) {
        if (export == null) {
            export = provider.getExport();
        }
        if (delay == null) {
            delay = provider.getDelay();
        }
    }
    if (export != null && ! export.booleanValue()) {
        return;
    }
    if (delay != null && delay > 0) {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(delay);
                } catch (Throwable e) {
                }
                doExport();
            }
        });
        thread.setDaemon(true);
        thread.setName("DelayExportServiceThread");
        thread.start();
    } else {
        doExport();
    }
}

由上面程式碼可知,如果設定了 delay 引數,Dubbo 的處理方式是啟動一個守護執行緒在 sleep 指定時間後再 doExport

2.3 引數檢查

ServiceConfig 的 doExport() 方法中會進行引數檢查和設定,包括:

  • 泛化呼叫
  • 本地實現
  • 本地存根
  • 本地偽裝
  • 配置(application、registry、protocol等)

ServiceConfig.java

protected synchronized void doExport() {
    if (unexported) {
        throw new IllegalStateException("Already unexported!");
    }
    if (exported) {
        return;
    }
    exported = true;
    if (interfaceName == null || interfaceName.length() == 0) {
        throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
    }
    checkDefault();
    //省略
    if (ref instanceof GenericService) {
        interfaceClass = GenericService.class;
        generic = true;
    } else {
        try {
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                    .getContextClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        checkInterfaceAndMethods(interfaceClass, methods);
        checkRef();
        generic = false;
    }
    if(local !=null){
        if(local=="true"){
            local=interfaceName+"Local";
        }
        Class<?> localClass;
        try {
            localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if(!interfaceClass.isAssignableFrom(localClass)){
            throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceName);
        }
    }
    if(stub !=null){
        if(stub=="true"){
            stub=interfaceName+"Stub";
        }
        Class<?> stubClass;
        try {
            stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if(!interfaceClass.isAssignableFrom(stubClass)){
            throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + interfaceName);
        }
    }
    //此處省略:檢查並設定相關引數
    doExportUrls();
}

2.4 多協議、多註冊中心

在檢查完引數之後,開始暴露服務。Dubbo 支援多協議和多註冊中心:

ServiceConfig.java

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

2.5 組裝URL

針對每個協議、每個註冊中心,開始組裝 URL

ServiceConfig.java

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    String name = protocolConfig.getName();
    if (name == null || name.length() == 0) {
        name = "dubbo";
    }

    //處理host

    //處理port

    Map<String, String> map = new HashMap<String, String>();
    //設定引數到map

    // 匯出服務
    String contextPath = protocolConfig.getContextpath();
    if ((contextPath == null || contextPath.length() == 0) && provider != null) {
        contextPath = provider.getContextpath();
    }
    URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);

    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }

    //此處省略:服務暴露(詳見 2.6 和 2.7)
    
    this.urls.add(url);
}

2.6 本地暴露

如果配置 scope=none, 則不會進行服務暴露;如果沒有配置 scope 或者 scope=local,則會進行本地暴露。

ServiceConfig.java

//public static final String LOCAL_PROTOCOL = "injvm";
//public static final String LOCALHOST = "127.0.0.1";

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    //......
    String scope = url.getParameter(Constants.SCOPE_KEY);
    //配置為none不暴露
    if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
        //配置不是remote的情況下做本地暴露 (配置為remote,則表示只暴露遠端服務)
        if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
            exportLocal(url);
        }
        //......
    }
    //......
}

private void exportLocal(URL url) {
    if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
        URL local = URL.valueOf(url.toFullString())
                .setProtocol(Constants.LOCAL_PROTOCOL)
                .setHost(NetUtils.LOCALHOST)
                .setPort(0);
        Exporter<?> exporter = protocol.export(
                proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
        exporters.add(exporter);
        logger.info("Export dubbo service " + interfaceClass.getName() +" to local registry");
    }
}

1. 暴露服務的時候,會通過代理建立 Invoker

2. 本地暴露時使用 injvm 協議,injvm 協議是一個偽協議,它不開啟埠,不能被遠端呼叫,只在 JVM 內直接關聯,但執行 Dubbo 的 Filter 鏈。

相關推薦

Dubbo原理原始碼解析服務暴露

github新增倉庫 "dubbo-read"(點此檢視),集合所有《Dubbo原理和原始碼解析》系列文章,後續將繼續補充該系列,同時將針對Dubbo所做的功能擴充套件也進行分享。不定期更新,歡迎Follow。 一、框架設計 在官方《Dubbo 使用者指南》架構部分,給出了服務呼叫的整體架構和

Dubbo原理原始碼解析服務引用

github新增倉庫 "dubbo-read"(點此檢視),集合所有《Dubbo原理和原始碼解析》系列文章,後續將繼續補充該系列,同時將針對Dubbo所做的功能擴充套件也進行分享。不定期更新,歡迎Follow。 一、框架設計 在官方《Dubbo 開發指南》框架設計部分,給出了引用服務時序圖:

Dubbo原理原始碼解析標籤解析

github新增倉庫 "dubbo-read"(點此檢視),集合所有《Dubbo原理和原始碼解析》系列文章,後續將繼續補充該系列,同時將針對Dubbo所做的功能擴充套件也進行分享。不定期更新,歡迎Follow。 一、Dubbo 配置方式 Dubbo 支援多種配置方式: XML 配置:基於

Dubbo原理原始碼解析“微核心+外掛”機制

private void loadFile(Map<String, Class<?>> extensionClasses, String dir) { //...... BufferedReader reader = new BufferedReader(new

Dubbo原始碼解析服務端接收訊息

準備 dubbo 版本:2.5.4 服務端接收訊息流程 Handler鏈路 DubboProtocol private ExchangeServer createServer(URL url) { url = url.addParameterIfAbsent("c

Dubbo原始碼解析服務釋出與註冊

準備 dubbo版本:2.5.4 Spring自定義擴充套件 dubbo 是基於 spring 配置來實現服務釋出,並基於 spring 的擴充套件機制定義了一套自定義標籤,要實現自定義擴充套件, spring 中提供了 NamespaceHandler 、BeanDefinit

dubbo原始碼解析服務export 遠端服務

前期回顧 sharding-jdbc原始碼解析 更新完畢 spring原始碼解析 更新完畢 spring-mvc原始碼解析 更新完畢 spring-tx原始碼解析 更新完畢 spring-boot原始碼解析 更新完畢 rocketmq原始碼解析 更新完畢 dubbo原始碼解析 更新

【NLP】Attention原理原始碼解析

對attention一直停留在淺層的理解,看了幾篇介紹思想及原理的文章,也沒實踐過,今天立個Flag,一天深入原理和原始碼!如果你也是處於attention model level one的狀態,那不妨好好看一下啦。 內容: 核心思想 原理解析(圖解+公式) 模型分類 優缺點 TF原始碼解析

Java執行緒池架構(一)原理原始碼解析

在前面介紹JUC的文章中,提到了關於執行緒池Execotors的建立介紹,在文章:《java之JUC系列-外部Tools》中第一部分有詳細的說明,請參閱; 文章中其實說明了外部的使用方式,但是沒有說內部是如何實現的,為了加深對實現的理解,在使用中可以放心,我們這裡將做原始碼解析以及反饋到原理上

演算法---hash演算法原理(java中HashMap底層實現原理原始碼解析)

散列表(Hash table,也叫雜湊表),是依據關鍵碼值(Key value)而直接進行訪問的資料結構。也就是說,它通過把關鍵碼值對映到表中一個位置來訪問記錄,以加快查詢的速度。這個對映函式叫做雜湊函式,存放記錄的陣列叫做散列表。  比如我們要儲存八十八個資料,我們為他申請了100個

jdk1.7的Java執行緒池架構原理原始碼解析(ThreadPoolExecutor)

我們現在來看看ThreadPoolExecutor的原始碼是怎麼樣的,也許你剛開始看他的原始碼會很痛苦,因為你不知道作者為什麼是這樣設計的,所以本文就我看到的思想會給你做一個介紹,此時也許你通過知道了一些作者的思想,你也許就知道應該該如何去操作了。 這裡來看下構造方法中對那

Java執行緒池架構原理原始碼解析(ThreadPoolExecutor)

在前面介紹JUC的文章中,提到了關於執行緒池Execotors的建立介紹,在文章:《java之JUC系列-外部Tools》中第一部分有詳細的說明,請參閱;文章中其實說明了外部的使用方式,但是沒有說內部是如何實現的,為了加深對實現的理解,在使用中可以放心,我們這裡將做原始碼解析

Spring Cloud系列(三):Eureka原始碼解析服務

一、自動裝配   1、根據自動裝配原理(詳見:Spring Boot系列(二):Spring Boot自動裝配原理解析),找到spring-cloud-starter-netflix-eureka-server.jar的spring.factories,檢視spring.factories如下:   2、進

Dubbo原始碼解析客戶端初始化及服務呼叫

準備 dubbo 版本:2.5.4 客戶端初始化過程 初始化過程 先上時序圖,幫助理解客戶端初始化過程。 ReferenceBean 是客戶端初始化入口,其實現 InitializingBean 介面,在 bean 初始化過程中會呼叫其 afterProper

9、dubbo原始碼分析 服務遠端暴露(中)

在上一篇文章我們講解了一下 dubbo 遠端服務暴露過程中通過 Netty 進行 Socket 服務暴露。使得遠端客戶端可以訪問這個暴露的服務,這個只是解決了訪問之前點到點的服務呼叫。對於分步式環境當中,越來越多的服務我們如何管理並且治理這些服務是一個問題。因此 dubbo

7、dubbo原始碼分析 服務本地暴露

在上一篇文章我們分析了一下 dubbo 在服務暴露發生了哪些事,今天我們就來分析一下整個服務暴露中的本地暴露。(PS:其實我感覺本地暴露蠻雞肋的)。本地暴露需要服務提供方與服務消費方在同一個 JVM。下面我們來寫一個本地暴露使用的例子: 1) DemoS

spring原始碼解析AOP原理

一、準備工作   在這裡我先簡單記錄下如何實現一個aop: AOP:【動態代理】 指在程式執行期間動態的將某段程式碼切入到指定方法指定位置進行執行的程式設計方式; 1、匯入aop模組;Spring AOP:(spring-aspects) 2、定義一個業務邏輯類(

Dubbo服務合買平臺搭建出售釋出服務暴露&心跳機制&服務註冊

Dubbo服務釋出 Dubbo合買平臺搭建出售 dsluntan.com Q:3393756370 VX:17061863513服務釋出影響流程的主要包括三個部分,依次是: 服務暴露 心跳 服務註冊 服務暴露是對外提供服務及暴露埠,以便消費端可以正常調通服務。心跳機制保證伺服器端及客戶

Spring原始碼解析 Spring Security啟動細節工作模式

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Dubbo原始碼解析LoadBalance負載均衡

閱讀須知 dubbo版本:2.6.0 spring版本:4.3.8 文章中使用/* */註釋的方法會做深入分析 正文 dubbo一共支援四種負載均衡策略,RoundRobinLoadBalance(輪詢)、RandomLoadBalance(隨機)、