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

提供者啟動(dòng)時(shí)向注冊(cè)中心注冊(cè)服務(wù)地址,消費(fèi)者啟動(dòng)時(shí)訂閱服務(wù),并通過獲取到的提供者地址發(fā)起調(diào)用,當(dāng)提供者地址變更時(shí),通過注冊(cè)中心向消費(fèi)者推送變更。這就是 dubbo 主要的工作流程。
在2.7.5之前,dubbo 只支持接口級(jí)服務(wù)發(fā)現(xiàn)模型,>=2.7.5的版本提供了接口級(jí)與應(yīng)用級(jí)兩種服務(wù)發(fā)現(xiàn)模型,3.0之后的版本應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)更是非常重要的一個(gè)功能。
本文將從為什么需要引入應(yīng)用級(jí)服務(wù)發(fā)現(xiàn),dubbo 實(shí)現(xiàn)應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)的難點(diǎn)以及dubbo3 是如何解決這些問題這三個(gè)部分進(jìn)行講解。
開始前,我們先了解下 dubbo 最初提供的接口級(jí)服務(wù)發(fā)現(xiàn)是怎樣的。
接口級(jí)服務(wù)發(fā)現(xiàn)長啥樣?
dubbo 服務(wù)的注冊(cè)發(fā)現(xiàn)是以接口為最小粒度的,在 dubbo 中將其抽象為一個(gè)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é)議代表提供服務(wù)的協(xié)議,如果注冊(cè)了 grpc 服務(wù),這里就是 grpc://
ip、port 代表是哪臺(tái)機(jī)器的哪個(gè)端口提供服務(wù)
interface 代表了注冊(cè)的接口名,它直接對(duì)應(yīng)到代碼中需要暴露服務(wù)的 interface,如下:
package org.newboo.basic.api;
import org.newboo.basic.model.User;
public interface MyDemoService {
void setUser(User user);
User getUser(String uid);
}
參數(shù)代表了服務(wù)的一些參數(shù),可能是元數(shù)據(jù),也可能是配置信息
細(xì)心的你一定發(fā)現(xiàn)了,一個(gè) interface 可以包含多個(gè) dubbo 接口,所以把它稱為接口級(jí)服務(wù)發(fā)現(xiàn)有些不妥,應(yīng)該是服務(wù)級(jí)服務(wù)發(fā)現(xiàn),但服務(wù)的定義比較模糊,可能會(huì)被誤認(rèn)為是應(yīng)用,甚至后面介紹的 dubbo 應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)使用的關(guān)鍵字也是 service,所以我們用接口級(jí)這個(gè)更加易懂的概念來代替。
接口級(jí)服務(wù)發(fā)現(xiàn)有什么問題?
數(shù)據(jù)太多
無論是存儲(chǔ)還是變更推送壓力都可能遇到瓶頸,數(shù)據(jù)多表現(xiàn)在這兩個(gè)方面:
注冊(cè)的單條數(shù)據(jù)太大
這個(gè)問題好解決:拆!
dubbo 在 2.7 之后的版本支持了元數(shù)據(jù)中心與配置中心,對(duì)于URL的參數(shù)進(jìn)行分類存儲(chǔ)。持久不變的(如application、method等)參數(shù)存儲(chǔ)到元數(shù)據(jù)中心中,可能在運(yùn)行時(shí)變化(timeout、tag)的存儲(chǔ)到配置中心中

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

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

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

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

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

最后
本文從接口級(jí)服務(wù)發(fā)現(xiàn)講到應(yīng)用級(jí)服務(wù)發(fā)現(xiàn),包含了為什么 dubbo 設(shè)計(jì)成接口級(jí)服務(wù)發(fā)現(xiàn),接口級(jí)服務(wù)發(fā)現(xiàn)有什么痛點(diǎn)?基于 dubbo 現(xiàn)狀如何設(shè)計(jì)應(yīng)用級(jí)服務(wù)發(fā)現(xiàn),應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)實(shí)現(xiàn)有什么難點(diǎn)等等問題進(jìn)行解答,相信看完的小伙伴一定有所收獲。
搜索關(guān)注微信公眾號(hào)"捉蟲大師",后端技術(shù)分享,架構(gòu)設(shè)計(jì)、性能優(yōu)化、源碼閱讀、問題排查、踩坑實(shí)踐。
