小白也能看懂的dubbo3應用級服務發(fā)現(xiàn)詳解
dubbo 是一款開源的 RPC 框架,主要有3個角色:提供者(provider)、消費者(consumer) 、注冊中心(registry)

提供者啟動時向注冊中心注冊服務地址,消費者啟動時訂閱服務,并通過獲取到的提供者地址發(fā)起調(diào)用,當提供者地址變更時,通過注冊中心向消費者推送變更。這就是 dubbo 主要的工作流程。
在2.7.5之前,dubbo 只支持接口級服務發(fā)現(xiàn)模型,>=2.7.5的版本提供了接口級與應用級兩種服務發(fā)現(xiàn)模型,3.0之后的版本應用級服務發(fā)現(xiàn)更是非常重要的一個功能。
本文將從為什么需要引入應用級服務發(fā)現(xiàn),dubbo 實現(xiàn)應用級服務發(fā)現(xiàn)的難點以及dubbo3 是如何解決這些問題這三個部分進行講解。
開始前,我們先了解下 dubbo 最初提供的接口級服務發(fā)現(xiàn)是怎樣的。
接口級服務發(fā)現(xiàn)長啥樣?
dubbo 服務的注冊發(fā)現(xiàn)是以接口為最小粒度的,在 dubbo 中將其抽象為一個URL,大概長這樣:
dubbo://10.1.1.123:20880/org.newboo.basic.api.MyDemoService?anyhost=true&application=ddog-my-demo&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.newboo.basic.api.MyDemoService&methods=setUser,getUser&owner=roshilikang&release=2.7.6&side=provider&threads=500
看著很亂?捋一捋:

協(xié)議代表提供服務的協(xié)議,如果注冊了 grpc 服務,這里就是 grpc://
ip、port 代表是哪臺機器的哪個端口提供服務
interface 代表了注冊的接口名,它直接對應到代碼中需要暴露服務的 interface,如下:
package?org.newboo.basic.api;
import?org.newboo.basic.model.User;
public?interface?MyDemoService?{
????void?setUser(User?user);
????User?getUser(String?uid);
}
參數(shù)代表了服務的一些參數(shù),可能是元數(shù)據(jù),也可能是配置信息
細心的你一定發(fā)現(xiàn)了,一個 interface 可以包含多個 dubbo 接口,所以把它稱為接口級服務發(fā)現(xiàn)有些不妥,應該是服務級服務發(fā)現(xiàn),但服務的定義比較模糊,可能會被誤認為是應用,甚至后面介紹的 dubbo 應用級服務發(fā)現(xiàn)使用的關鍵字也是 service,所以我們用接口級這個更加易懂的概念來代替。
接口級服務發(fā)現(xiàn)有什么問題?
數(shù)據(jù)太多
無論是存儲還是變更推送壓力都可能遇到瓶頸,數(shù)據(jù)多表現(xiàn)在這兩個方面:
注冊的單條數(shù)據(jù)太大
這個問題好解決:拆!
dubbo 在 2.7 之后的版本支持了元數(shù)據(jù)中心與配置中心,對于URL的參數(shù)進行分類存儲。持久不變的(如application、method等)參數(shù)存儲到元數(shù)據(jù)中心中,可能在運行時變化(timeout、tag)的存儲到配置中心中

注冊數(shù)據(jù)條數(shù)太多

無論是增加一臺機器還是增加一個接口,其增長都是線性的,這個問題比單條數(shù)據(jù)大更嚴重。
當抹去注冊信息中的 interface 信息,這樣數(shù)據(jù)量就大大減少

非主流
只用過 dubbo 的同學可能覺得這很主流。
但從服務發(fā)現(xiàn)的角度來看:
無論是用的最多的服務注冊發(fā)現(xiàn)系統(tǒng) DNS,又或者是 SpringCloud 體系、K8S 體系,都是以應用為維度進行服務注冊發(fā)現(xiàn)的,只有和這些體系對齊,才能更好地與之進行打通。
在我了解的范圍里,目前只有 dubbo、SOFARPC、HSF 三個阿里系的 RPC 框架支持了接口級的服務發(fā)現(xiàn)。
接口級服務發(fā)現(xiàn)如何使用
provider端暴露服務:
<dubbo:registry?address="zookeeper://127.0.0.1:2181"/>
<dubbo:service?interface="org.newboo.basic.api.MyDemoService"?ref="myNewbooDemoService"/>
consumer端引用服務:
<dubbo:registry?address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference?id="myNewbooDemoService"?interface="org.newboo.basic.api.MyDemoService"/>

本地調(diào)用遠程的方法時,只需要配置一個 reference,然后直接使用 interface 來調(diào)用,我們不必去實現(xiàn)這個 interaface,dubbo 自動幫我們生成了一個代理進行 RPC 調(diào)用,屏蔽了通信的細節(jié),讓我們有種像調(diào)用本地方法一樣調(diào)用遠程方法的感覺,這也是 dubbo 的優(yōu)勢。
從這里我們能看出為什么 dubbo 要設計成接口級服務發(fā)現(xiàn),因為要為每一個 interface 生成一個代理,就必須定位到該 interface 對應服務暴露的服務地址,為了方便,dubbo 就這么設計了。
如果要實現(xiàn)應用級服務發(fā)現(xiàn)你會怎么做?
如果讓我來設計應用級服務發(fā)現(xiàn),注冊不必多說,按應用名注冊即可。
但這里有個隱藏問題是應用名的唯一性,應用名必須得很好的管理起來,否則重復、隨意改動都可能導致服務發(fā)現(xiàn)失效
至于訂閱,在目前 dubbo 機制下,必須得告訴消費者消費的每個接口是屬于哪個應用,這樣才能定位到接口部署在哪里。
難點是什么
實現(xiàn) dubbo 應用級服務發(fā)現(xiàn),難點在于
兼容性,除了服務發(fā)現(xiàn),其他改動點盡量少,且能兼容接口級到應用級的過渡 接口到應用的部署關系,在接口級服務發(fā)現(xiàn)中,是不需要關心接口部署在哪個應用上的,但換做應用級,必須得知道這點,但這點就增加了開發(fā)者的使用難度,有沒有方案盡量屏蔽細節(jié)?

dubbo3 是如何解決這些問題的?
兼容性
保留接口級服務發(fā)現(xiàn),且默認采取雙注冊方式,可配置使用哪種服務發(fā)現(xiàn)模型,如下配置使用應用級服務發(fā)現(xiàn)
<dubbo:registry?address="zookeeper://127.0.0.1:2181?registry-type=service"/>
居然使用了service這個關鍵字??
如何查找接口對應的應用
方案1:手動配置,實現(xiàn)簡單,架構簡單,但用戶使用成本高,這種方式 dubbo3 已支持
<dubbo:service?services="ddog-my-demo-p0"?interface="org.newboo.basic.api.MyDemoService"?ref="myNewbooDemoService"/>
方案2:服務自省
名詞有點高大上,但道理很簡單,讓 dubbo 自己去匹配,提供者注冊的時候把接口和應用名的映射關系存儲起來,消費者消費時根據(jù)接口名獲取到部署的應用名,再去做服務發(fā)現(xiàn)。
數(shù)據(jù)存儲在哪里?顯然元數(shù)據(jù)中心非常合適。該方案用戶使用起來和之前接口級沒有任何不同,但需要增加一個元數(shù)據(jù)中心,架構變得復雜。
且有一個問題是,如果接口在多個應用下部署了,dubbo 查找的策略是都去訂閱,這可能在某些場景下不太合適。

最后
本文從接口級服務發(fā)現(xiàn)講到應用級服務發(fā)現(xiàn),包含了為什么 dubbo 設計成接口級服務發(fā)現(xiàn),接口級服務發(fā)現(xiàn)有什么痛點?基于 dubbo 現(xiàn)狀如何設計應用級服務發(fā)現(xiàn),應用級服務發(fā)現(xiàn)實現(xiàn)有什么難點等等問題進行解答,相信看完的小伙伴一定有所收獲。
