搜尋關注微信公眾號"捉蟲大師",後端技術分享,架構設計、效能優化、原始碼閱讀、問題排查、踩坑實踐。
本文已收錄 https://github.com/lkxiaolou/lkxiaolou 歡迎star。
dubbo 是一款開源的 RPC 框架,主要有3個角色: 提供者(provider)、消費者(consumer) 、註冊中心(registry)
提供者啟動時向註冊中心註冊服務地址,消費者啟動時訂閱服務,並通過獲取到的提供者地址發起呼叫,當提供者地址變更時,通過註冊中心向消費者推送變更。這就是 dubbo 主要的工作流程。
在2.7.5之前,dubbo 只支援介面級服務發現模型,>=2.7.5的版本提供了介面級與應用級兩種服務發現模型,3.0之後的版本應用級服務發現更是非常重要的一個功能。
本文將從為什麼需要引入應用級服務發現,dubbo 實現應用級服務發現的難點以及dubbo3 是如何解決這些問題這三個部分進行講解。
開始前,我們先了解下 dubbo 最初提供的介面級服務發現是怎樣的。
介面級服務發現長啥樣?
dubbo 服務的註冊發現是以介面為最小粒度的,在 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
看著很亂?捋一捋:
協議代表提供服務的協議,如果註冊了 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);
}
- 引數代表了服務的一些引數,可能是元資料,也可能是配置資訊
細心的你一定發現了,一個 interface 可以包含多個 dubbo 介面,所以把它稱為介面級服務發現有些不妥,應該是服務級服務發現,但服務的定義比較模糊,可能會被誤認為是應用,甚至後面介紹的 dubbo 應用級服務發現使用的關鍵字也是 service,所以我們用介面級這個更加易懂的概念來代替。
介面級服務發現有什麼問題?
資料太多
無論是儲存還是變更推送壓力都可能遇到瓶頸,資料多表現在這兩個方面:
- 註冊的單條資料太大
這個問題好解決:拆!
dubbo 在 2.7 之後的版本支援了元資料中心與配置中心,對於URL的引數進行分類儲存。持久不變的(如application、method等)引數儲存到元資料中心中,可能在執行時變化(timeout、tag)的儲存到配置中心中
- 註冊資料條數太多
無論是增加一臺機器還是增加一個介面,其增長都是線性的,這個問題比單條資料大更嚴重。
當抹去註冊資訊中的 interface 資訊,這樣資料量就大大減少
非主流
只用過 dubbo 的同學可能覺得這很主流。
但從服務發現的角度來看:
無論是用的最多的服務註冊發現系統 DNS,又或者是 SpringCloud 體系、K8S 體系,都是以應用為維度進行服務註冊發現的,只有和這些體系對齊,才能更好地與之進行打通。
在我瞭解的範圍裡,目前只有 dubbo、SOFARPC、HSF 三個阿里系的 RPC 框架支援了介面級的服務發現。
介面級服務發現如何使用
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"/>
本地呼叫遠端的方法時,只需要配置一個 reference,然後直接使用 interface 來呼叫,我們不必去實現這個 interaface,dubbo 自動幫我們生成了一個代理進行 RPC 呼叫,遮蔽了通訊的細節,讓我們有種像呼叫本地方法一樣呼叫遠端方法的感覺,這也是 dubbo 的優勢。
從這裡我們能看出為什麼 dubbo 要設計成介面級服務發現,因為要為每一個 interface 生成一個代理,就必須定位到該 interface 對應服務暴露的服務地址,為了方便,dubbo 就這麼設計了。
如果要實現應用級服務發現你會怎麼做?
如果讓我來設計應用級服務發現,註冊不必多說,按應用名註冊即可。
但這裡有個隱藏問題是應用名的唯一性,應用名必須得很好的管理起來,否則重複、隨意改動都可能導致服務發現失效
至於訂閱,在目前 dubbo 機制下,必須得告訴消費者消費的每個介面是屬於哪個應用,這樣才能定位到介面部署在哪裡。
難點是什麼
實現 dubbo 應用級服務發現,難點在於
- 相容性,除了服務發現,其他改動點儘量少,且能相容介面級到應用級的過渡
- 介面到應用的部署關係,在介面級服務發現中,是不需要關心介面部署在哪個應用上的,但換做應用級,必須得知道這點,但這點就增加了開發者的使用難度,有沒有方案儘量遮蔽細節?
dubbo3 是如何解決這些問題的?
相容性
保留介面級服務發現,且預設採取雙註冊方式,可配置使用哪種服務發現模型,如下配置使用應用級服務發現
<dubbo:registry address="zookeeper://127.0.0.1:2181?registry-type=service"/>
居然使用了service這個關鍵字
如何查詢介面對應的應用
- 方案1:手動配置,實現簡單,架構簡單,但使用者使用成本高,這種方式 dubbo3 已支援
<dubbo:service services="ddog-my-demo-p0" interface="org.newboo.basic.api.MyDemoService" ref="myNewbooDemoService"/>
- 方案2:服務自省
名詞有點高大上,但道理很簡單,讓 dubbo 自己去匹配,提供者註冊的時候把介面和應用名的對映關係儲存起來,消費者消費時根據介面名獲取到部署的應用名,再去做服務發現。
資料儲存在哪裡?顯然元資料中心非常合適。該方案使用者使用起來和之前介面級沒有任何不同,但需要增加一個元資料中心,架構變得複雜。
且有一個問題是,如果介面在多個應用下部署了,dubbo 查詢的策略是都去訂閱,這可能在某些場景下不太合適。
最後
本文從介面級服務發現講到應用級服務發現,包含了為什麼 dubbo 設計成介面級服務發現,介面級服務發現有什麼痛點?基於 dubbo 現狀如何設計應用級服務發現,應用級服務發現實現有什麼難點等等問題進行解答,相信看完的小夥伴一定有所收穫。