【全球報(bào)資訊】聊聊服務(wù)發(fā)現(xiàn)的推拉模型
前言
過去一年,我的工作重心投入到了 API 網(wǎng)關(guān)(阿里云 CSB)中,這對于我來說是一個(gè)新的領(lǐng)域,但和之前接觸的微服務(wù)治理方向又密不可分。API 網(wǎng)關(guān)適配微服務(wù)場景需要完成一些基礎(chǔ)能力的建設(shè),其一便是對接注冊中心,從而作為微服務(wù)的入口流量,例如 Zuul、SpringCloud Gateway 都實(shí)現(xiàn)了這樣的功能。實(shí)際上很多開源網(wǎng)關(guān)在這一特性上均存在較大的局限性,本文暫不討論這些局限性,而是針對服務(wù)發(fā)現(xiàn)這一通用的場景,分享我對它的一些思考。
(資料圖)
概念澄清服務(wù)發(fā)現(xiàn)這個(gè)詞說實(shí)話還是有點(diǎn)抽象的,在微服務(wù)這一特定的領(lǐng)域具象化討論才有意義?!胺?wù)發(fā)現(xiàn)”指的是“服務(wù)消費(fèi)者獲取服務(wù)提供者服務(wù)地址”的這一過程,而“服務(wù)”這一名詞在不同微服務(wù)框架中代指也可能有所不同,不過大多數(shù)都是代指的應(yīng)用、接口等信息。
SpringCloud 以應(yīng)用維度表示服務(wù)Dubbo2.x 以接口維度表示服務(wù);Dubbo3.x 以應(yīng)用維度表示服務(wù)服務(wù)從 Provider -> Registry -> Consumer 的這一流動過程便是本文重點(diǎn)討論的內(nèi)容。
數(shù)據(jù)傳遞的兩種方式:推模型和拉模型,一直是老生常談的話題,在服務(wù)發(fā)現(xiàn)中也不妨一談。先不要急著回答服務(wù)發(fā)現(xiàn)這一場景中,推拉到底誰好的問題,讓我們先看看一些微服務(wù)框架中的服務(wù)發(fā)現(xiàn)是如何實(shí)現(xiàn)的。
微服務(wù)框架中的服務(wù)發(fā)現(xiàn)這一節(jié)以 Dubbo 和 SpringCloud 兩個(gè)微服務(wù)框架為引子,看看它們的服務(wù)發(fā)現(xiàn)到底使用的是拉模型還是推模型。
Dubbo 服務(wù)發(fā)現(xiàn)publicinterfaceRegistryService{voidregister(URLurl);voidunregister(URLurl);/***Subscribetoeligibleregistereddataandautomaticallypushwhentheregistereddataischanged.*/voidsubscribe(URLurl,NotifyListenerlistener);voidunsubscribe(URLurl,NotifyListenerlistener);Listlookup(URLurl);}
Dubbo 管理服務(wù)發(fā)現(xiàn)的核心接口 RegistryService直接給出了答案,通過 subscribe 和 notify 這些關(guān)鍵字便可以猜測到 Dubbo 使用的是推模型。
上圖是一個(gè)推模型的工作流程。
SpringCloud 服務(wù)發(fā)現(xiàn)publicinterfaceDiscoveryClientextendsOrdered{intDEFAULT_ORDER=0;Stringdescription();ListgetInstances(StringserviceId);List getServices();defaultvoidprobe(){this.getServices();}defaultintgetOrder(){return0;}}
DiscoveryClient是 SpringCloud 中一個(gè)核心服務(wù)發(fā)現(xiàn)的接口,通過 getInstances基本可以看出,SpringCloud 使用的是拉模型。
上圖是一個(gè)拉模型的工作流程。
盡管我們還沒有詳細(xì)領(lǐng)略到兩個(gè)模型背后的優(yōu)化和實(shí)現(xiàn)細(xì)節(jié),但從事實(shí)來看,Dubbo 和 SpringCloud 使用了不同的服務(wù)發(fā)現(xiàn)機(jī)制,都能讓微服務(wù)玩轉(zhuǎn)起來。
此時(shí),如果你心里已經(jīng)有了 Dubbo 是推模型,SpringCloud 是拉模型的認(rèn)知,不妨再繼續(xù)看下一節(jié),可能這樣的認(rèn)知又會有了動搖。
注冊中心中的推拉上一節(jié)站在微服務(wù)框架的角度,介紹了服務(wù)發(fā)現(xiàn)的推拉模型,這一節(jié)則是站在注冊中心的角度來分析。說到底,無論是 Dubbo 還是 SpringCloud,總得對接一款注冊中心才可以獲得服務(wù)發(fā)現(xiàn)的能力,可以是 Zookeeper,可以是 Nacos,可以是 Eureka,也可以是任意的其他提供了服務(wù)發(fā)現(xiàn)能力的組件。
我就先以 Nacos 為例介紹下它的推拉模型。先看 Nacos 的 Naming 模塊提供的核心接口:
publicinterfaceNamingService{voidregisterInstance(StringserviceName,Stringip,intport)throwsNacosException;voidderegisterInstance(StringserviceName,Stringip,intport)throwsNacosException;ListgetAllInstances(StringserviceName,booleansubscribe)throwsNacosException;List selectInstances(StringserviceName,StringgroupName,booleanhealthy,booleansubscribe)throwsNacosException;voidsubscribe(StringserviceName,EventListenerlistener)throwsNacosException;voidunsubscribe(StringserviceName,List clusters,EventListenerlistener)throwsNacosException;}
為了方便閱讀,我刪除了大部分重載的接口以及非核心的接口??梢园l(fā)現(xiàn),從 API 角度,Nacos 是同時(shí)提供了推模型和拉模型兩套接口的,這樣也是方便其被微服務(wù)框架集成,有興趣的讀者,可以自行去閱讀下 Dubbo/SpringCloud Alibaba 集成 Nacos 的代碼,Dubbo 使用的便是 subscribe 這一套推模型的接口,SpringCloud Alibaba 則是使用的 selectInstances 這一套拉模型的接口。
那是否說"Nacos 是一個(gè)推拉模型結(jié)合的注冊中心"呢,不夠嚴(yán)謹(jǐn)。且看 getAllInstances,selectInstances 這兩個(gè)方法都有一個(gè) subscribe 入?yún)ⅲ幌略创a探究一下
publicListselectInstances(StringserviceName,StringgroupName,List clusters,booleanhealthy,booleansubscribe)throwsNacosException{ServiceInfoserviceInfo;if(subscribe){serviceInfo=hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName,groupName),StringUtils.join(clusters,","));}else{serviceInfo=hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName,groupName),StringUtils.join(clusters,","));}returnselectInstances(serviceInfo,healthy);}
可以發(fā)現(xiàn) subscribe 這個(gè)參數(shù)控制的是是否直接從注冊中心拉取服務(wù),subscribe=false 時(shí),實(shí)際是從 Nacos 自身維護(hù)的一塊本地緩存中獲取到的服務(wù),大多數(shù)情況下,獲取服務(wù)使用的是 subscribe=true 的重載方法。所以,selectInstance 看起來是在拉服務(wù),subscribe 看起來是在推服務(wù),實(shí)際上 Nacos 內(nèi)核維護(hù)緩存的方式我們并未得知。
從上述 subscribe 的提示中,我們可以得出結(jié)論,我們并不能直接通過個(gè)別接口就得出該注冊中心使用的是推模型還是拉模型,究竟何種模型,還是要看 client 端是如何從 server 端加載/更新服務(wù)信息的。
那么,真實(shí)情況下,Nacos cleint 究竟是如何從 server 端獲取到服務(wù)列表的呢?也不賣關(guān)子了,直接給結(jié)論
在 Nacos 1.x 中,Nacos 采用的是定時(shí)拉 + udp 推送的機(jī)制??蛻舳藭右粋€(gè)定時(shí)器,每 10s 拉取一次服務(wù),確保服務(wù)端服務(wù)版本一致,為了解決 10s 間隔內(nèi)服務(wù)更新了,客戶端卻沒有及時(shí)收到通知的這一問題,Nacos 還在服務(wù)端服務(wù)更新時(shí),觸發(fā)了一次 udp 推送。
在 Nacos 2.x 中,Nacos 采用的是服務(wù)端 tcp 推送的機(jī)制。客戶端啟動時(shí)會跟服務(wù)端建立一條 tcp 長連接,服務(wù)端服務(wù)變更后,會復(fù)用客戶端建立的這條連接進(jìn)行數(shù)據(jù)推送。
所以在回答,Nacos 到底是推模型還是拉模型時(shí),需要區(qū)分版本來回答。
結(jié)論:Nacos 1.x 是拉模型;Nacos 2.x 是推模型
不知道有沒有讀者好奇 Nacos 為什么這么設(shè)計(jì),我簡單用一些 QA 快速解答一些可能的疑問:
Q:為什么 Nacos 1.x 使用了 udp 推送,卻把 Nacos 1.x 定義為拉模型?
A:Nacos 1.x 中 udp 推送主要是為了降低服務(wù)更新延時(shí)而設(shè)計(jì)的,并且在復(fù)雜網(wǎng)絡(luò)部署架構(gòu)中,例如 client 與 server 只能單向訪問,或者有 SLB 中間介質(zhì)時(shí),udp 就會失效;且 udp 本身就是不穩(wěn)定的,Nacos 嘗試兩次失敗后就會放棄推送。所以主要還是在用拉模式來保障。
Q:為什么 Nacos 1.x 一開始不使用 Nacos 2.x 中的架構(gòu),使用 tcp 推送?
A:個(gè)人猜測是因?yàn)槔P蛯?shí)現(xiàn)起來簡單,Nacos 2.x 才引入了 grpc 實(shí)現(xiàn)長連接
Q:為什么 Nacos 1.x 的服務(wù)發(fā)現(xiàn)使用的是短輪詢,不像配置中心那樣使用長輪詢?
A:在服務(wù)發(fā)現(xiàn)場景中,服務(wù)端比較在意內(nèi)存消耗,長輪詢雖然不會占用線程,但服務(wù)端依舊會 hold 住 request/response,造成不必要的內(nèi)存浪費(fèi)。
一些常見注冊中心的推拉模型:
Zookeeper:推模型Nacos 1.x:拉模型Nacos 2.x:推模型Eureka:拉模型好,介紹完了注冊中心視角的服務(wù)發(fā)現(xiàn)推拉模型了,再回過頭來看一個(gè)問題:如果使用了 SpringCloud Alibaba + Nacos 2.1 版本,那它的服務(wù)發(fā)現(xiàn)就是走的哪種模型呢?
正確答案:在微服務(wù)框架視角,sca 走的是拉模型這種同步拉取服務(wù)的機(jī)制;在注冊中心視角,應(yīng)用作為客戶端是使用的推模型在接收服務(wù)變更的推送。
那有一部分比較帥的人可能會有問了,到底什么模型比較好呢?哎,下面就容我對比下二者。
推拉對比實(shí)時(shí)性服務(wù)推送的實(shí)時(shí)性是服務(wù)發(fā)現(xiàn)主要的 SLA 指標(biāo),其指的是當(dāng)服務(wù)地址發(fā)生變化時(shí),客戶端感知到變化的延遲。試想一下,服務(wù)端正在發(fā)布,IP 地址發(fā)生了變化,但是由于地址推送不及時(shí),客戶端過了 10 分鐘還在調(diào)用舊的服務(wù)地址,這是多么可怕的一件事。
拉模型感知服務(wù)變化的延遲便是短輪詢間隔+拉取服務(wù)的耗時(shí),在 Nacos 中,SLA 為 10s。
推模型感知服務(wù)變化的延遲則為服務(wù)端推送服務(wù)的耗時(shí),在 Nacos 中,SLA 為 1s。
在實(shí)時(shí)性上,推模型有較大優(yōu)勢。
壓力拉模型和推模型都會對服務(wù)端造成壓力,但是二者的時(shí)機(jī)不同。
拉模型的壓力是固定的,取決于輪詢間隔。推模型的壓力取決于服務(wù)變更的頻率。用兩個(gè)場景來做對比:
常態(tài)化場景。日常運(yùn)行時(shí),服務(wù)列表一般不會變化,拉模型會導(dǎo)致不必要的開銷,對服務(wù)端造成較大壓力??焐峡煜聢鼍?。機(jī)器快速擴(kuò)容或者縮容,導(dǎo)致服務(wù)地址頻繁變更,推送量會瞬時(shí)變大,對服務(wù)端和客戶端造成較大壓力。針對快上快下場景,也可以進(jìn)行一系列的優(yōu)化,例如推送合并,增量推送,數(shù)據(jù)分離等等,目前 Nacos 支持了推送合并這一優(yōu)化。
代碼復(fù)雜度碼齡越大,越發(fā)意識到代碼復(fù)雜度是一個(gè)非常重要的技術(shù)選型指標(biāo),越簡單的代碼越容易維護(hù),也具備持久的生命力,架構(gòu)師需要在代碼復(fù)雜度和性能的 trade off 中,找到一個(gè)平衡點(diǎn),不得不承認(rèn),推模型的復(fù)雜度往往要比拉模型高出很多,例如多出了長連接的狀態(tài)管理這一環(huán)節(jié)。
拉模型完全勝出。
總結(jié)這篇文章不希望大家陷入到字眼中,判斷某一個(gè)框架或者工具是推或者拉模型,而是希望能介紹清楚服務(wù)發(fā)現(xiàn)中推拉模型的工作流程,方便大家對這些微服務(wù)框架也好,注冊中心也好,有一個(gè)更深的理解。
總結(jié)一下,主流的微服務(wù)框架和注冊中心的服務(wù)發(fā)現(xiàn)機(jī)制中,推模型和拉模型均有使用,具體如何選擇,如何優(yōu)化,可以根據(jù)自身服務(wù)的特點(diǎn),以及服務(wù)的規(guī)模去選擇使用。
在我負(fù)責(zé)的 API 網(wǎng)關(guān)(阿里云 CSB)中,采用了一套獨(dú)立的服務(wù)發(fā)現(xiàn)機(jī)制,同時(shí)支持拉模型和推模型,以適配部分僅支持推模型或者僅支持拉模型的注冊中心。
相關(guān)閱讀
-
【全球報(bào)資訊】聊聊服務(wù)發(fā)現(xiàn)的推拉模型
前言過去一年,我的工作重心投入到了API網(wǎng)關(guān)(阿里云CSB)中,這對... -
素顏哈小兔“兔Too可愛”微信表情上線
哈小兔【得償所愿】微信表情上線啦昨天上線了“得償所愿”微信表情... -
哈小兔【得償所愿】微信表情上線啦 環(huán)...
之前分享了使用支付寶的AI年畫功能,制作了一款“哈小兔”紅包封面... -
看熱訊:《心若菩提》曹德旺
?《心若菩提》是玻璃大王曹德旺的一本傳記。這本書買了一段時(shí)間了... -
產(chǎn)品思維知識點(diǎn)|環(huán)球快看點(diǎn)
?記錄一些學(xué)習(xí)產(chǎn)品知識過程中的知識點(diǎn),未來圍繞這些點(diǎn),搭建系統(tǒng)... -
短訊!研發(fā)團(tuán)隊(duì)技術(shù)氛圍建設(shè)
?很多研發(fā)同學(xué)希望自己團(tuán)隊(duì)有很強(qiáng)的技術(shù)氛圍,這點(diǎn)我個(gè)人是比較認(rèn)...