微服務(wù)現(xiàn)在是一個很火的話題,好像不管項目的大小,適用范圍都在往微服務(wù)上去靠。這也使得現(xiàn)在如果不會微服務(wù)出去都沒法和別人聊了。
僅從我自己的工作經(jīng)歷來看,盡管我們的項目也是微服務(wù)化了的。但是說實(shí)話在業(yè)務(wù)開發(fā)過程中并沒有體會到微服務(wù)的開發(fā)和單體項目開發(fā)中存在很大的區(qū)別(僅業(yè)務(wù)開發(fā)這一塊來說)。
微服務(wù)的學(xué)習(xí)單獨(dú)的寫幾個demo并沒有啥用,現(xiàn)在的框架封裝程度都很高,就算是代碼跑起來來依舊是云里霧里。微服務(wù)的學(xué)習(xí)更多的是搞清楚微服務(wù)中每一個組件到底是什么?為什么有這些組件?沒有會怎么樣?功能大概是怎么實(shí)現(xiàn)的?
搞清楚來這些基本上就可以說對微服務(wù)有個大體的掌握了。
為什么需要注冊中心
在微服務(wù)中首先需要面對的問題就是不同的服務(wù)之間如何進(jìn)行通信呢?在單體服務(wù)中如果不同的服務(wù)之間需要通信,一般就是服務(wù)將接口暴露,然后其他的服務(wù)通過http進(jìn)行請求,那么很明顯在微服務(wù)中也可以這樣。
但這里存在的問題在于單體服務(wù)中我們需要請求的目標(biāo)是我們在請求的url中直接寫死的,因?yàn)榉?wù)不多可以這樣。而微服務(wù)中需要考慮的是存在大量服務(wù)時手動維護(hù)服務(wù)列表是否合適?如果服務(wù)橫向擴(kuò)展時如何通知其他的服務(wù)?服務(wù)宕機(jī)后,如何及時下線等等問題。
注冊中心的功能
注冊中心的存在是為了更好更方便的管理應(yīng)用中的每一個服務(wù),是各個分布式節(jié)點(diǎn)之間的紐帶。注冊中心主要提供以下核心功能:
- 服務(wù)注冊與發(fā)現(xiàn):動態(tài)的增減服務(wù)節(jié)點(diǎn),服務(wù)節(jié)點(diǎn)增減后動態(tài)的通知服務(wù)消費(fèi)者,而不需要由消費(fèi)者來更新配置。
- 服務(wù)配置:動態(tài)修改服務(wù)配置,并將其推送到服務(wù)提供者和服務(wù)消費(fèi)者而不需要重啟服務(wù)。
- 健康檢查和服務(wù)摘除:主動的檢查服務(wù)健康情況,對于宕機(jī)的服務(wù)將其摘除服務(wù)列表。
注冊中心的實(shí)現(xiàn)
注冊中心的主要功能就是保存服務(wù)的具體信息,然后由服務(wù)消費(fèi)者讀取這些信息。從整體的流程上來說大概就是這樣:
除了將服務(wù)注冊到注冊中心,實(shí)際還存在服務(wù)的反注冊。
目前對注冊中心的實(shí)現(xiàn)分為兩種模式,分別為客戶端(應(yīng)用內(nèi)注冊)和服務(wù)端(應(yīng)用外注冊)。
客戶端注冊中心
目前客戶端實(shí)現(xiàn)的注冊中心比較有代表性的就是eureka,雖然eureka2.0版本在此前宣布閉源,但目前好像沒有太大的影響。后續(xù)的話可以考慮阿里開源的nacos。
eureka是一個基于JAVA語言實(shí)現(xiàn)的費(fèi)用與服務(wù)發(fā)現(xiàn)與注冊的組件,包含服務(wù)端和客戶端兩部分。
服務(wù)端主要用來存儲服務(wù)信息,定時進(jìn)行服務(wù)檢查。而客戶端用于完成向服務(wù)端注冊服務(wù)信息以及從服務(wù)端拉取服務(wù)信息等。
上圖是eureka官給出的eureka的架構(gòu)圖。
從圖中可以很明顯的看到eureka的服務(wù)端也是作為一個服務(wù)部署,而客戶端則是通過sdk的方式接入應(yīng)用。客戶端向服務(wù)端進(jìn)行服務(wù)注冊以及更新服務(wù)等,同時客戶端從服務(wù)端獲取服務(wù)信息,不同服務(wù)之間根據(jù)獲得的服務(wù)信息進(jìn)行遠(yuǎn)程調(diào)用。
為了滿足服務(wù)的高可用,通過移動多個eureka server服務(wù),不同eureka server相互注冊實(shí)現(xiàn)服務(wù)的高可用。
服務(wù)端注冊中心
eureka是由注冊中心提供服務(wù)端和客戶端的SDK,業(yè)務(wù)端通過引入注冊中心提供的SDK,來實(shí)現(xiàn)服務(wù)的注冊和發(fā)現(xiàn)。而服務(wù)端的注冊中心則是通過應(yīng)用外的組件來實(shí)現(xiàn)服務(wù)注冊功能的。目前用的比較多的服務(wù)端注冊中心組件如zookeeper、consul等。這里以zookeeper為例。
Zookeeper 是 Apache Hadoop 的子項目,是一個樹型的目錄服務(wù),支持變更推送,適合作為 Dubbo 服務(wù)的注冊中心,工業(yè)強(qiáng)度較高,可用于生產(chǎn)環(huán)境,是目前Dubbo官方主推搭配的注冊中心。
上圖是dubbo官網(wǎng)提供的zookeeper作為注冊中心的基本原理。
以dubbo為根目錄,創(chuàng)建一服務(wù)接口全限定名的目錄,在其下創(chuàng)建存放服務(wù)提供者接口信息的providers目錄、存放服務(wù)消費(fèi)者接口信息的consumers目錄、存放服務(wù)配置信息的configurators目錄等。
服務(wù)提供者啟動的時候在providers下創(chuàng)建一條服務(wù)信息,該信息可以被consumer獲取。通過zk提供的watcher機(jī)制注冊一個watcher在服務(wù)提供者的providers目錄下,這樣當(dāng)服務(wù)出現(xiàn)擴(kuò)容或者宕機(jī)的時候就可以立即被consumer消費(fèi)。
注冊中心的問題
在微服務(wù)中,注冊中心作為一個存儲所有服務(wù)中心的地方必然不可能是單機(jī)的。因?yàn)槿绻灾行臒o法使用,那么服務(wù)提供者就無法對外暴露自己的服務(wù),消費(fèi)者也無法獲取自己需要調(diào)用的服務(wù)的具體地址,整個應(yīng)用將會崩潰。首先需要保證的就是注冊中心的高可用。
一致性和可用性的抉擇
說到高可用,CAP理論是繞不過去的,CAP簡單來說就是我們需要在服務(wù)的可用性和服務(wù)間的一致性進(jìn)行一個抉擇。是犧牲可用性來保證強(qiáng)一致性還是保證可用性犧牲強(qiáng)一致性呢?
CP類型注冊中心
Zookeeper是一個典型的CP類型的高可用組件。zookeeper實(shí)現(xiàn)來paxos算法,zookeeper集群有一個節(jié)點(diǎn)作為leader,如果leader節(jié)點(diǎn)掛了zk會通過ZAB協(xié)議來進(jìn)行l(wèi)eader選舉,但是在選舉的過程中zk是不能對外提供服務(wù)的。
而且當(dāng)因?yàn)榫W(wǎng)絡(luò)分區(qū)導(dǎo)致zk集群出現(xiàn)腦裂問題時,由于ZAB協(xié)議需要半數(shù)以上的節(jié)點(diǎn)參與,所以可能會有部分或者全部的zk節(jié)點(diǎn)無法對外提供服務(wù)。但是zk可以保證所有可用節(jié)點(diǎn)都數(shù)據(jù)都是一致,即使節(jié)點(diǎn)崩潰,在恢復(fù)后也會和其他節(jié)點(diǎn)保持一致,這就是zk犧牲了可用性而保證的強(qiáng)一致性。
AP類型注冊中心
而類似與eureka的注冊中心則是AP類型的。eureka實(shí)現(xiàn)高可用是通過啟動多個eureka server服務(wù),每一個eureka server即是提供者也是消費(fèi)者,每個eureka server將自己作為服務(wù)注冊給其他的eureka server,這樣每個eureka server都是其他server 的replica。eureka這種模式中每個節(jié)點(diǎn)都是平等的,不存在leader、flower。
當(dāng)集群中某一個server節(jié)點(diǎn)宕機(jī),請求可以直接轉(zhuǎn)移到其他節(jié)點(diǎn)。即使發(fā)生了網(wǎng)絡(luò)分區(qū),盡管不同分區(qū)之間無法進(jìn)行通信,但是在每一個分區(qū)內(nèi)的節(jié)點(diǎn)是通信并且可以正常對外提供服務(wù),這樣就保證了在server節(jié)點(diǎn)宕機(jī)的情況下的注冊中心的可用性。
而問題在于由于不同分區(qū)無法通信,就導(dǎo)致可能一部分節(jié)點(diǎn)的數(shù)據(jù)和另一部分有差別,這就是犧牲了強(qiáng)一致性來換取系統(tǒng)可用性。
注冊中心的選擇
首先我們應(yīng)該明確的是其實(shí)注冊中心的存在與否應(yīng)該是不能直接影響服務(wù)的調(diào)用的。服務(wù)之間的調(diào)用時通過http或者rpc等協(xié)議直接調(diào)用的,注冊中心只是提供一個調(diào)用地址。服務(wù)是否可以正常使用不能直接靠注冊中心節(jié)點(diǎn)信息來決定。
即使注冊中心出現(xiàn)問題,只要服務(wù)本身沒有宕機(jī),理論上來說還是應(yīng)該正常對外提供服務(wù)。所以很明顯對于我們來說AP類型的注冊中心是要優(yōu)于CP類型的注冊中心的。
當(dāng)然在實(shí)際實(shí)現(xiàn)過程中很多框架都會盡可能的去修補(bǔ)這些問題,比如eureka會定時的同步各個節(jié)點(diǎn)的數(shù)據(jù),盡可能的做到數(shù)據(jù)一致性。dubbo的zk注冊中心實(shí)現(xiàn)過程中也會本地緩存服務(wù)信息并不是完全通過zk信息來決定是否剔除服務(wù)等。所以實(shí)際在選擇時還是需要根據(jù)自身實(shí)際以及是否還有其他需求來確定。
混合語言開發(fā)
在微服務(wù)中,每一個服務(wù)都是一個獨(dú)立的整體。各個服務(wù)之間并不向單體應(yīng)用中的不同模塊大都要求是同一語言實(shí)現(xiàn)。現(xiàn)如今各大公司開發(fā)語言是多樣化的,不同的業(yè)務(wù)線開發(fā)的語言可能都不盡相同,混合語言是我們必須要面對的一個問題,所以出現(xiàn)了多語言之間服務(wù)調(diào)用的問題。
對于像eureka這樣的應(yīng)用內(nèi)的注冊中心。由于服務(wù)的注冊與發(fā)現(xiàn)都是依賴于SDK,所以如果要使用eureak那需要對應(yīng)的語言實(shí)現(xiàn)的SDK,目前有不少語言都有提供如Python、node.js。目前springclould的Sidecar組件也可以做到將其他語言納入到springclould體系中來。
對于應(yīng)用外的注冊中心,一般會由這些中間件提供客戶端操作實(shí)現(xiàn)。然后由語言本身來實(shí)現(xiàn)服務(wù)注冊和治理等功能。目前很多語言實(shí)際上也有相關(guān)的開源工程。
節(jié)點(diǎn)存活判斷
前面已經(jīng)說過了保證CP類型的強(qiáng)一致性注冊中心以及AP類型保證可用性的注冊中心。而對于一個注冊中心來說最重要的就是管理其中的節(jié)點(diǎn)信息。服務(wù)啟動的時候需要將服務(wù)信息記錄,當(dāng)服務(wù)出現(xiàn)問題的時候需要將服務(wù)摘除。
但是問題在于如何實(shí)現(xiàn)這個摘除的操作,目前注冊中心的實(shí)現(xiàn)都是通過心跳檢測來判斷服務(wù)的健康狀態(tài)。正常情況下如果服務(wù)宕機(jī)了,心跳檢測無法和服務(wù)通信判斷該節(jié)點(diǎn)無法正常服務(wù),將服務(wù)從注冊中心摘除,這個過程是沒有問題的。但是如果發(fā)生網(wǎng)絡(luò)問題,比如網(wǎng)絡(luò)抖動或者網(wǎng)絡(luò)分區(qū),這時候心跳檢測肯定是無法正常通信的,但如果就因此直接將服務(wù)摘除那肯定是有問題的,因?yàn)楣?jié)點(diǎn)實(shí)際上是能夠正常提供服務(wù)的。所以需要有機(jī)制來確保這個過程不會粗暴的將節(jié)點(diǎn)從注冊中心摘除。
客戶端判斷
目前很多的框架實(shí)現(xiàn)都會在本地緩存微服務(wù)的節(jié)點(diǎn)信息。實(shí)際上使用這些節(jié)點(diǎn)的是各個服務(wù),服務(wù)是否能正常使用,這些節(jié)點(diǎn)才是最有發(fā)言權(quán)的。
客戶端緩存了節(jié)點(diǎn)信息,當(dāng)服務(wù)端判定服務(wù)出現(xiàn)問題后發(fā)出更新請求時,客戶端并不立刻刪除,可以先做一個標(biāo)識。后續(xù)的業(yè)務(wù)請求過來后,仍舊判定該服務(wù)時可用的,只有當(dāng)發(fā)出的請求無法收到正常回應(yīng)時才將該服務(wù)摘除。由客戶端來決定節(jié)點(diǎn)是否可用,不過這需要容錯機(jī)制來支持。
動態(tài)設(shè)置心跳檢測
在網(wǎng)絡(luò)頻繁抖動的情況下,注冊中心的節(jié)點(diǎn)信息會快速變化,也會給客戶端發(fā)送大量的信息,當(dāng)服務(wù)較多的時候可能會有大量的帶寬被占用導(dǎo)致正常的請求都無法處理。
所以需要一種動態(tài)的調(diào)整注冊中心心跳檢測頻率的機(jī)制,當(dāng)檢測到網(wǎng)絡(luò)抖動頻繁時,根據(jù)網(wǎng)絡(luò)情況調(diào)整心跳檢測的頻率,比如調(diào)整為正常情況下的1/2,10/1等。在網(wǎng)絡(luò)正常時心跳檢測也恢復(fù)正常。
小結(jié)
注冊中心是微服務(wù)架構(gòu)中一個很關(guān)鍵的組件,其保證來各個服務(wù)之間正常調(diào)用,以及服務(wù)的橫向擴(kuò)容等功能。在進(jìn)行注冊中心選型時需要考慮業(yè)務(wù)場景。
作者:不能摸魚啦
鏈接:
https://juejin.im/post/5ebe0ae36fb9a0432a3c43b6