十張圖搞懂服務(wù)注冊(cè)發(fā)現(xiàn)機(jī)制
在微服務(wù)架構(gòu)或分布式環(huán)境下,服務(wù)注冊(cè)與發(fā)現(xiàn)技術(shù)不可或缺,這也是程序員進(jìn)階之路必須要掌握的核心技術(shù)之一,本文通過(guò)圖解的方式帶領(lǐng)大家輕輕松松掌握。
引入服務(wù)注冊(cè)與發(fā)現(xiàn)組件的原因
先來(lái)看一個(gè)問(wèn)題,假如現(xiàn)在我們要做一個(gè)商城項(xiàng)目,作為架構(gòu)師的你應(yīng)該怎樣設(shè)計(jì)系統(tǒng)的架構(gòu)?你心里肯定在想:這還不容易直接照搬淘寶的架構(gòu)不就行了。但在現(xiàn)實(shí)的創(chuàng)業(yè)環(huán)境中一個(gè)項(xiàng)目可能是九死一生,如果一開(kāi)始投入巨大的人力和財(cái)力,一旦項(xiàng)目失敗損失就很大。
作為一位有經(jīng)驗(yàn)的架構(gòu)師需要結(jié)合公司財(cái)力、人力投入預(yù)算等現(xiàn)狀選擇最適合眼下的架構(gòu)才是王道。大型網(wǎng)站都是從小型網(wǎng)站發(fā)展而來(lái),架構(gòu)也是一樣。
任何一個(gè)大型網(wǎng)站的架構(gòu)都不是從一開(kāi)始就一層不變的,而是隨著用戶量和數(shù)據(jù)量的不斷增加不斷迭代演進(jìn)的結(jié)果。
在架構(gòu)不斷迭代演進(jìn)的過(guò)程中我們會(huì)遇到很多問(wèn)題,技術(shù)發(fā)展的本質(zhì)就是不斷發(fā)現(xiàn)問(wèn)題再解決問(wèn)題,解決問(wèn)題又發(fā)現(xiàn)問(wèn)題。
單體架構(gòu)
在系統(tǒng)建立之初可能不會(huì)有特別多的用戶,將所有的業(yè)務(wù)打成一個(gè)應(yīng)用包放在tomcat容器中運(yùn)行,與數(shù)據(jù)庫(kù)共用一臺(tái)服務(wù)器,這種架構(gòu)一般稱之為單體架構(gòu)。
在初期這種架構(gòu)的效率非常高,根據(jù)用戶的反饋可以快速迭代上線。但是隨著用戶量增加,一臺(tái)服務(wù)的內(nèi)存和CPU吃緊,很容易造成瓶頸,新的問(wèn)題來(lái)了怎么解決呢?
應(yīng)用與數(shù)據(jù)分離
隨著用戶請(qǐng)求量增加,一臺(tái)服務(wù)器的內(nèi)存和CPU持續(xù)飆升,用戶請(qǐng)求響應(yīng)時(shí)間變慢。這時(shí)候可以考慮將應(yīng)用與數(shù)據(jù)庫(kù)拆開(kāi),各自使用一臺(tái)服務(wù)器,你看問(wèn)題又解決了吧。
突然有一天掃地阿姨不小心碰了電線,其中一臺(tái)服務(wù)器掉電了,用戶所有的請(qǐng)求都報(bào)錯(cuò),隨之而來(lái)的是一系列投訴電話。
集群部署
單實(shí)例很容易造成單點(diǎn)問(wèn)題,比如遇到服務(wù)器故障或者服務(wù)能力瓶頸,那怎么辦?聰明的你肯定想到了,用集群呀。
集群部署是指將應(yīng)用部署在多個(gè)服務(wù)器或者虛機(jī)上,用戶通過(guò)服務(wù)均衡隨機(jī)訪問(wèn)其中的一個(gè)實(shí)例,從而使多個(gè)實(shí)例的流量均衡,如果一個(gè)實(shí)例出現(xiàn)故障可以將其下線,其他實(shí)例不受影響仍然可以對(duì)外提供服務(wù)。
隨著用戶數(shù)量快速增加,老板決定增加投入擴(kuò)大團(tuán)隊(duì)規(guī)模。開(kāi)發(fā)團(tuán)隊(duì)壯大后效率并沒(méi)有得到顯著的提高,以前小團(tuán)隊(duì)可以一周迭代上線一次,現(xiàn)在至少需要兩到三周時(shí)間。
業(yè)務(wù)邏輯越來(lái)越復(fù)雜,代碼間耦合很嚴(yán)重,修改一行代碼可能引入幾個(gè)線上問(wèn)題。架構(gòu)師意識(shí)到需要進(jìn)行架構(gòu)重構(gòu)。
微服務(wù)架構(gòu)
當(dāng)單體架構(gòu)演進(jìn)到一定階段后開(kāi)發(fā)測(cè)試的復(fù)雜性都會(huì)成本增加,團(tuán)隊(duì)規(guī)模的擴(kuò)大也會(huì)使得各自工作耦合性更嚴(yán)重,牽一發(fā)而動(dòng)全身就是這種場(chǎng)景。
單體架構(gòu)遇到瓶頸了,微服務(wù)架構(gòu)就橫空出世了。微服務(wù)就是將之前的單體服務(wù)按照業(yè)務(wù)維度進(jìn)行拆分,拆分粒度可大可小,拆分時(shí)機(jī)可以分節(jié)奏進(jìn)行。最佳實(shí)踐是先將一些獨(dú)立的功能從單體中剝離出來(lái)抽成一個(gè)或多個(gè)微服務(wù),這樣可以保障業(yè)務(wù)的連續(xù)性和穩(wěn)定性。
如上圖將一個(gè)商用應(yīng)用拆分為六個(gè)獨(dú)立微服務(wù)。六個(gè)微服務(wù)可以使用Docker容器化進(jìn)行多實(shí)例部署。
架構(gòu)演化到這里遇到了一個(gè)難題,如果要查詢用戶所有的訂單,用戶服務(wù)可能會(huì)依賴訂單服務(wù),用戶服務(wù)如何與訂單服務(wù)交互呢?訂單服務(wù)有多個(gè)實(shí)例該訪問(wèn)哪一個(gè)?
通常有幾種解決辦法:
(1)服務(wù)地址硬編碼
服務(wù)的地址寫(xiě)死在數(shù)據(jù)庫(kù)或者配置文件,通過(guò)訪問(wèn)DNS域名進(jìn)行尋址路由。
服務(wù)B的地址硬編碼在數(shù)據(jù)庫(kù)或者配置文件中,服務(wù)A首先需要拿到服務(wù)B的地址,然后通過(guò)DNS服務(wù)器解析獲取其中一實(shí)例的真實(shí)地址,最后可以向服務(wù)B發(fā)起請(qǐng)求。
如果遇到大促活動(dòng)需要對(duì)服務(wù)實(shí)例擴(kuò)容,大促完需要對(duì)服務(wù)實(shí)例進(jìn)行下線,運(yùn)維人員要做大量的手工操作,非常容易誤操作。
(2)服務(wù)動(dòng)態(tài)注冊(cè)與發(fā)現(xiàn)
服務(wù)地址硬編碼還有一個(gè)非常致命的問(wèn)題,如果一臺(tái)實(shí)例掛了,運(yùn)維人員可能不能及時(shí)感知到,導(dǎo)致一部分用戶的請(qǐng)求會(huì)異常。
引入服務(wù)注冊(cè)與發(fā)現(xiàn)組件可以很好解決上面遇到的問(wèn)題,避免過(guò)多的人工操作。
架構(gòu)演進(jìn)總結(jié)
在單體架構(gòu)中一個(gè)應(yīng)用程序就是一個(gè)服務(wù)包,包內(nèi)的模塊通過(guò)函數(shù)方法相互調(diào)用,模型足夠簡(jiǎn)單,根本沒(méi)有服務(wù)注冊(cè)和發(fā)現(xiàn)一說(shuō)。
在微服務(wù)架構(gòu)中會(huì)將一個(gè)應(yīng)用程序拆分為多個(gè)微服務(wù),微服務(wù)會(huì)部署在不同的服務(wù)器、不同的容器、甚至多數(shù)據(jù)中心,微服務(wù)間要相互調(diào)用,服務(wù)注冊(cè)和發(fā)現(xiàn)成為了一個(gè)不可或缺的組件。
服務(wù)注冊(cè)與發(fā)現(xiàn)基本原理
服務(wù)注冊(cè)與發(fā)現(xiàn)是分為注冊(cè)和發(fā)現(xiàn)兩個(gè)關(guān)鍵的步驟。
服務(wù)注冊(cè):服務(wù)進(jìn)程在注冊(cè)中心注冊(cè)自己的元數(shù)據(jù)信息。通常包括主機(jī)和端口號(hào),有時(shí)還有身份驗(yàn)證信息,協(xié)議,版本號(hào),以及運(yùn)行環(huán)境的信息。
服務(wù)發(fā)現(xiàn):客戶端服務(wù)進(jìn)程向注冊(cè)中心發(fā)起查詢,來(lái)獲取服務(wù)的信息。服務(wù)發(fā)現(xiàn)的一個(gè)重要作用就是提供給客戶端一個(gè)可用的服務(wù)列表。
服務(wù)注冊(cè)
服務(wù)注冊(cè)有兩種形式:客戶端注冊(cè)和代理注冊(cè)。
客戶端注冊(cè)
客戶端注冊(cè)是服務(wù)自己要負(fù)責(zé)注冊(cè)與注銷的工作。當(dāng)服務(wù)啟動(dòng)后注冊(cè)線程向注冊(cè)中心注冊(cè),當(dāng)服務(wù)下線時(shí)注銷自己。
這種方式的缺點(diǎn)是注冊(cè)注銷邏輯與服務(wù)的業(yè)務(wù)邏輯耦合在一起,如果服務(wù)使用不同語(yǔ)言開(kāi)發(fā),那需要適配多套服務(wù)注冊(cè)邏輯。
代理注冊(cè)
代理注冊(cè)由一個(gè)單獨(dú)的代理服務(wù)負(fù)責(zé)注冊(cè)與注銷。當(dāng)服務(wù)提供者啟動(dòng)后以某種方式通知代理服務(wù),然后代理服務(wù)負(fù)責(zé)向注冊(cè)中心發(fā)起注冊(cè)工作。
這種方式的缺點(diǎn)是多引用了一個(gè)代理服務(wù),并且代理服務(wù)要保持高可用狀態(tài)。
服務(wù)發(fā)現(xiàn)
服務(wù)發(fā)現(xiàn)也分為客戶端發(fā)現(xiàn)和代理發(fā)現(xiàn)。
客戶端發(fā)現(xiàn)
客戶端發(fā)現(xiàn)是指客戶端負(fù)責(zé)向注冊(cè)中心查詢可用服務(wù)地址,獲取到所有的可用實(shí)例地址列表后客戶端根據(jù)負(fù)載均衡算法選擇一個(gè)實(shí)例發(fā)起請(qǐng)求調(diào)用。
這種方式非常直接,客戶端可以控制負(fù)載均衡算法。但是缺點(diǎn)也很明顯,獲取實(shí)例地址、負(fù)載均衡等邏輯與服務(wù)的業(yè)務(wù)邏輯耦合在一起,如果服務(wù)發(fā)現(xiàn)或者負(fù)載平衡有變化,那么所有的服務(wù)都要修改重新上線。
代理發(fā)現(xiàn)
代理發(fā)現(xiàn)是指新增一個(gè)路由服務(wù)負(fù)責(zé)服務(wù)發(fā)現(xiàn)獲取可用的實(shí)例列表,服務(wù)消費(fèi)者如果需要調(diào)用服務(wù)A的一個(gè)實(shí)例可以直接將請(qǐng)求發(fā)往路由服務(wù),路由服務(wù)根據(jù)配置好的負(fù)載均衡算法從可用的實(shí)例列表中選擇一個(gè)實(shí)例將請(qǐng)求轉(zhuǎn)發(fā)過(guò)去即可,如果發(fā)現(xiàn)實(shí)例不可用,路由服務(wù)還可以自行重試,服務(wù)消費(fèi)者完全不用感知。
心跳機(jī)制
如果服務(wù)有多個(gè)實(shí)例,其中一個(gè)實(shí)例出現(xiàn)宕機(jī),注冊(cè)中心是可以實(shí)時(shí)感知到,并且將該實(shí)例信息從列表中移出,也稱為摘機(jī)。
如何實(shí)現(xiàn)摘機(jī)?業(yè)界比較常用的方式是通過(guò)心跳檢測(cè)的方式實(shí)現(xiàn),心跳檢測(cè)有主動(dòng)和被動(dòng)兩種方式。
被動(dòng)檢測(cè)是指服務(wù)主動(dòng)向注冊(cè)中心發(fā)送心跳消息,時(shí)間間隔可自定義,比如配置5秒發(fā)送一次,注冊(cè)中心如果在三個(gè)周期內(nèi)比如說(shuō)15秒內(nèi)沒(méi)有收到實(shí)例的心跳消息,就會(huì)將該實(shí)例從列表中移除。
上圖中服務(wù)A的實(shí)例2已經(jīng)宕機(jī)不能主動(dòng)給注冊(cè)中心發(fā)送心跳消息,15秒之后注冊(cè)就會(huì)將實(shí)例2移除掉。
主動(dòng)檢測(cè)是注冊(cè)中心主動(dòng)發(fā)起,每隔幾秒中會(huì)給所有列表中的服務(wù)實(shí)例發(fā)送心跳檢測(cè)消息,如果多個(gè)周期內(nèi)未發(fā)送成功或未收到回復(fù)就會(huì)主動(dòng)移除該實(shí)例。