再來填個坑,聊一聊Dubbo應(yīng)用級服務(wù)注冊的實現(xiàn)原理
大家好,我是三友~~
在之前寫的7000字+22張圖探秘Dubbo一次RPC調(diào)用的核心流程這篇閱讀量非常感人的文章中我留了一個小坑。
圖片
在發(fā)文之前我也猜到了這篇文章閱讀量大概率會很感人,所以壓根兒覺得不可能完成。但是結(jié)果是,閱讀量雖然如預(yù)期所料,但是點贊量卻著實給了個小驚喜。
圖片
既然現(xiàn)在已經(jīng)滿38個了,那么我這就來把這個坑給填一填。講一講Dubbo在3.x版本之后為什么使用應(yīng)用級服務(wù)注冊以及它背后的實現(xiàn)原理。
還是一個簡單的Demo
同樣地,為了保證文章的完整性和連貫性,防止你忘記了,我把之前那篇文章的Demo再拿過去。
如果你還記得,可直接跳過本節(jié),直接進入下一節(jié)
在Dubbo中RPC調(diào)用過程中主要分為以下兩個角色:
- 服務(wù)提供者:提供一個接口給消費者遠程調(diào)用
- 服務(wù)消費者:調(diào)用生產(chǎn)者提供的接口
一個簡單的Dubbo示例工程就如下所示:
Demo中Dubbo使用的是3.0.7的版本,Nacos使用的是2.3.2版本,代碼地址 https://github.com/sanyou3/dubbo-demo.git接口層,提供者消費者都需要依賴,服務(wù)提供者實現(xiàn),服務(wù)消費者調(diào)用;
圖片
服務(wù)提供者單獨一個工程,實現(xiàn)DemoService接口,通過@DubboService表明提供DemoService這個服務(wù);
圖片
服務(wù)提供者配置文件;
圖片
服務(wù)消費者單獨一個工程,這里使用單元測試,通過@DubboReference注解表明消費DemoService這個服務(wù)接口;
圖片
服務(wù)消費者配置文件;
圖片
啟動服務(wù)提供者,運行消費者單元測試,結(jié)果如下:
成功實現(xiàn)遠程服務(wù)調(diào)用
應(yīng)用級服務(wù)注冊和接口級服務(wù)注冊
1、應(yīng)用級服務(wù)注冊
談到應(yīng)用級服務(wù)注冊,其實我們都很了解
就比如說在SpringCloud環(huán)境下,服務(wù)實例在啟動的時候會將自身的服務(wù)名、IP、端口外加一些其它的數(shù)據(jù)注冊到注冊中心,但是在這一過程中并不會將服務(wù)的接口信息注冊到注冊中心。所以對于服務(wù)調(diào)用者(消費者)來說,它也只能從注冊中心獲取到服務(wù)名、IP和端口這些信息,無法獲取到服務(wù)提供者提供了哪些接口,這就是應(yīng)用級服務(wù)注冊,如下圖所示
圖片
所以應(yīng)用級服務(wù)注冊用一句話概括就是:
一個服務(wù)不管對外提供了多少接口,它都是作為一個整體注冊到服務(wù)注冊中心
2、接口級服務(wù)注冊
接口級服務(wù)注冊就跟應(yīng)用級服務(wù)注冊相反了。接口級服務(wù)注冊就是把每個單獨的接口看成一個服務(wù)進行注冊。所以服務(wù)在啟動的時候,每個接口都將作為單獨的服務(wù)注冊到注冊中心。也就是說,有幾個接口,就有幾個服務(wù),就注冊幾次。Dubbo在2.x版本的時候就是使用的接口級服務(wù)注冊。所以在前面的Demo中,你可以在注冊中心中看到如下接口級服務(wù)注冊的服務(wù)信息。
圖片
Dubbo3.x兼容2.x接口級服務(wù)注冊,所以也能看到
當然也包括接口的詳細信息;
圖片
消費者在訂閱的時候也就是訂閱所需要消費的接口對應(yīng)的服務(wù)信息。所以接口級服務(wù)注冊用一句話概括就是。
以接口為單位進行服務(wù)注冊和發(fā)現(xiàn),每個服務(wù)的每個接口都單獨注冊和發(fā)現(xiàn)
為什么Dubbo在3.x版本要使用應(yīng)用級服務(wù)注冊
之所以Dubbo在3.x版本之后放棄使用接口級服務(wù)注冊,轉(zhuǎn)而使用應(yīng)用級服務(wù)注冊,主要包括以下兩點原因:
第一點就是接口級服務(wù)注冊會增加注冊中心的壓力。
壓力主要來自三個部分:
- 服務(wù)注冊的壓力
- 服務(wù)變更通知的壓力
- 服務(wù)數(shù)據(jù)存儲的壓力
對于應(yīng)用級服務(wù)來說,一個服務(wù)實例只需要注冊1次。但是對于接口級服務(wù)注冊來說,有多少接口就得注冊多少次,如果有100個,那么就得注冊100次。這就大大提高了注冊中心服務(wù)注冊的壓力。至于服務(wù)變更通知的壓力,這也很好理解。我們都知道,服務(wù)注冊中心一般都有一個服務(wù)數(shù)據(jù)變更通知的功能。當有服務(wù)實例注冊時,注冊中心會去通知訂閱了這個服務(wù)的其它服務(wù),告訴其它服務(wù)所訂閱的服務(wù)實例數(shù)據(jù)有變更。
圖片
對于應(yīng)用級服務(wù)來說,一個服務(wù)實例上線只需要給另一個訂閱了該服務(wù)的服務(wù)實例推送1次就可以了。但是對于接口級服務(wù)注冊來說,如果服務(wù)提供者有100個接口,服務(wù)消費者訂閱了這100個接口服務(wù)。那么一個服務(wù)實例上線,注冊中心需要給另一個服務(wù)消費者推送100次服務(wù)變更的消息。這就造成了注冊中心服務(wù)變更推送的壓力。至于第三個就更好理解了,注冊的服務(wù)數(shù)據(jù)變多了,那么存儲的壓力就會變大。所以這么一對比就可以清晰的得出一個結(jié)論
接口級服務(wù)注冊的壓力遠遠大于服務(wù)級注冊的壓力
這就是為什么要換成應(yīng)用級服務(wù)注冊的第一個原因。至于第二個原因,就聽起來這就比較高大上了。主要是為了向SpringCloud和K8S等生態(tài)靠齊。因為SpringCloud和K8S它們其實都是應(yīng)用級的注冊和發(fā)現(xiàn)。所以為了更好的融入SpringCloud和K8S的生態(tài)。Dubbo在3.x就開始轉(zhuǎn)向應(yīng)用級的服務(wù)注冊和發(fā)現(xiàn)。如果非得換一句逼格高的措辭來表示,那就是對現(xiàn)代微服務(wù)架構(gòu)和云原生技術(shù)趨勢的適應(yīng)和支持。
Dubbo3.x應(yīng)用級服務(wù)注冊的實現(xiàn)原理
前面鋪墊完了,接下來我們就來講一講Dubbo3.x應(yīng)用級服務(wù)注冊的實現(xiàn)原理。雖然采用了應(yīng)用級服務(wù)注冊,但是Dubbo的本質(zhì)并沒有改變。依然還是使用接口來調(diào)用。所以對于消費者來說,還是必須得知道接口的詳情數(shù)據(jù),包括接口所在服務(wù)器的IP、端口、通信協(xié)議等等。但是現(xiàn)在注冊中心只有應(yīng)用級服務(wù)信息,并沒有接口級服務(wù)信息,怎么獲取呢?
Dubbo將整個實現(xiàn)總共拆為兩步:
- 消費者需要先獲取消費的接口所在的服務(wù)名
- 消費者通過獲取到服務(wù)名再去獲取接口詳情數(shù)據(jù)
1、接口是哪個服務(wù)提供的?
首先第一步,消費者需要先獲取消費的接口所在的服務(wù)名,那么問題來了。
消費者如何去獲取到消費的接口所在的服務(wù)名?
但是當你仔細思考一下時,你其實會發(fā)現(xiàn)這并不算是一個問題,因為很簡單,服務(wù)提供者和服務(wù)消費者一般都我們自己開發(fā)的服務(wù),所以我們肯定知道接口在哪個服務(wù)上,就像OpenFeign一樣,我們每次使用時都會自己指明接口所在的服務(wù)。
圖片
所以Dubbo也給我們提供了3種配置方式,讓我們可以手動指定接口所在的服務(wù)。
第一種,使用@DubboReference#providedBy屬性配置。
圖片
第二種,通過消費者配置文件配置接口所在的服務(wù)名。
圖片
第三種,在消費者配置文件注冊中心的配置中加上接口所在的服務(wù)名
圖片
當你加了這些配置的時候,Dubbo就認為消費的接口就在這些服務(wù)中,雖然通過配置可以指定,但是我不知道你有沒有發(fā)現(xiàn),前面演示的Demo中我并沒有進行任何配置,也能調(diào)用成功。所以除了這種配置的方式之外,Dubbo還提供了第二種方式,叫做服務(wù)名自動探測。服務(wù)提供者在啟動的時候,將接口全限定名以及當前服務(wù)名的映射關(guān)系存到一個中間的地方,而消費者只需要根據(jù)消費的接口到這個中間的地方就可以查到接口所在的服務(wù)名,這個中間的地方在Dubbo中被稱為元數(shù)據(jù)中心。
圖片
這里你肯定有一個疑問?
元數(shù)據(jù)中心又是什么?
其實元數(shù)據(jù)中心僅僅是一個概念上的東西,只要可以存數(shù)據(jù),都可以被稱為元數(shù)據(jù)中心。
Dubbo默認支持三種組件作為元數(shù)據(jù)中心:
- Redis
- Nacos
- Zookeeper
當你使用Nacos或者Zookeeper作為注冊中心時,Dubbo會默認使用它們作為元數(shù)據(jù)中心(當然也可以禁用)。并且Nacos使用的是它配置中心的功能。所以在前面的Demo中你就可以在Nacos配置中心模塊中看到下面這條接口和服務(wù)名的映射數(shù)據(jù)。
圖片
Group是mapping,也就是映射的意思。這就是為什么Demo中沒有配置服務(wù)提供者也可以調(diào)用成功的原因。到這我們來總結(jié)一下消費者知道所消費的接口在哪個服務(wù)上的兩種方式:
手動配置,有三種不同的方式
- @DubboReference#providedBy屬性配置。
- 通過消費者配置文件配置接口所在的服務(wù)名。
- 在消費者配置文件注冊中心的配置中加上接口所在的服務(wù)名。
自動探測
服務(wù)提供者啟動時將自身所提供的接口和服務(wù)名的映射關(guān)系存到元數(shù)據(jù)中心。服務(wù)消費者在啟動的時候,會去從元數(shù)據(jù)中心查到自己所消費的接口屬于哪個服務(wù)。自動探測方式需要引入元數(shù)據(jù)中心,使用Nacos或者Zookeeper作為注冊中心時,Dubbo默認會使用它們作為元數(shù)據(jù)中心。如果項目中沒有使用元數(shù)據(jù)中心,那么只能使用第一種手動配置的方式。
2、服務(wù)接口詳情數(shù)據(jù)如何獲取?
通過上一節(jié)的方式我們可以成功知道消費者所消費的接口在哪個服務(wù)上。但是僅僅知道接口在哪個服務(wù)上還是無法調(diào)用。因為必須得知道接口使用IP、端口、通信協(xié)議等。所以消費者此時就會進行第二步,獲取接口詳情數(shù)據(jù)。Dubbo也提供了兩種獲取方式。第一種,從服務(wù)提供者本地緩存中獲取,這種方式也是默認的。對于接口服務(wù)提供者來說,它會將接口詳情數(shù)據(jù)存到本地緩存。所以消費者可以從服務(wù)本地緩存中獲取,入下圖所示:
圖片
但是有一個問題,怎么獲取呢?Dubbo做的就很巧妙了。
首先服務(wù)提供者在啟動的時候,會去啟動并暴露一個接口是MetadataServiceRPC接口服務(wù)。
圖片
之后在注冊服務(wù)實例的時候,會將暴露出去的MetadataService這個RPC接口的協(xié)議和端口一起存到注冊中心,如下圖所示:
圖片
默認使用的端口就是20880,通信協(xié)議是Dubbo協(xié)議。由于經(jīng)過第一步之后,消費者已經(jīng)知道接口在哪個服務(wù)上了。
所以就可以從注冊中心中獲取這個服務(wù)對應(yīng)的服務(wù)實例信息。也就能知道MetadataService這個RPC接口所在的服務(wù)器IP、端口、通信協(xié)議。之后消費者就可以通過這些信息,構(gòu)建RPC請求,從服務(wù)提供者獲取到接口的詳細數(shù)據(jù)。
整個過程如下圖所示:
圖片
還有一點,之所以說默認是從本地緩存中獲取,是因為在服務(wù)實例信息中還存在這么一條信息。
圖片
dubbo.metadata.storage-type=local
消費者在獲取接口詳情數(shù)據(jù)時,會先判斷dubbo.metadata.storage-type這個屬性值是多少。如果是local,那么就按照前面說的從服務(wù)提供者本地緩存中獲取。當然這個配置還可以在服務(wù)提供者的配置文件中按照如下方式進行修改。
圖片
如果改成remote,那么應(yīng)該從哪獲取呢?這就對應(yīng)第二種情況了,我們接著往下看。
第二種情況也會用到元數(shù)據(jù)中心。
服務(wù)提供者在啟動的時候,會將接口的詳情數(shù)據(jù)全部存到元數(shù)據(jù)中心。對于消費者來說,只需要從元數(shù)據(jù)中心就可以獲取到接口的詳情數(shù)據(jù)了。
圖片
所以在前面的Demo中你就可以在Nacos配置中心模塊中看到下面這條包含所有接口詳情數(shù)據(jù)的配置。
圖片
小總結(jié)
到這就講完了Dubbo3.x應(yīng)用級服務(wù)注冊的實現(xiàn)原理。
這里我畫一張圖再從整體總結(jié)一下前面提到的整個過程。
圖片
首先第一步,需要知道接口在哪個服務(wù)上,總共有兩種辦法:
- 手動配置
- 通過服務(wù)接口從元數(shù)據(jù)中心獲取所在的服務(wù)名
當僅僅知道接口在哪個服務(wù)上還是無法調(diào)用,必須知道接口的詳情數(shù)據(jù)。
接口的詳情數(shù)據(jù)可以存在兩個地方:
- 服務(wù)提供者的本地緩存
- 元數(shù)據(jù)中心
對于消費者來說,首先得知道應(yīng)該是從服務(wù)提供者的本地緩存還是元數(shù)據(jù)中心種獲取。所以消費者會先根據(jù)第一步獲取到的服務(wù)名從服務(wù)注冊中心獲取服務(wù)實例信息。
因為服務(wù)提供者注冊的時候會攜帶dubbo.metadata.storage-type屬性,告訴消費者應(yīng)該從哪獲取。默認是服務(wù)提供者的本地緩存,可通過配置修改。
消費者會根據(jù)所配置的屬性值通過對應(yīng)的方式獲取到接口的詳情數(shù)據(jù)。之后就可以基于這些接口的詳情信息發(fā)送接口級別的RPC調(diào)用了。