原文鏈接:Service Discovery in a Microservices Architecture
- 微服務(wù)介紹
- 構(gòu)建微服務(wù)之使用API網(wǎng)關(guān)
- 構(gòu)建微服務(wù)之:微服務(wù)架構(gòu)中的進(jìn)程間通信
- 微服務(wù)中的服務(wù)發(fā)現(xiàn)(本文)
- 微服務(wù)之事件驅(qū)動(dòng)的數(shù)據(jù)管理
- 選擇一種微服務(wù)部署策略
- 重構(gòu)單體應(yīng)用到微服務(wù)
這是使用微服務(wù)構(gòu)建應(yīng)用的第四篇文章。第一篇文章介紹了微服務(wù)架構(gòu)模式并討論了使用微服務(wù)的優(yōu)勢(shì)和劣勢(shì),該系列的第二和第三篇文章 描述了微服務(wù)架構(gòu)中通信的不同方面,本篇文章我們將密切討論下服務(wù)發(fā)現(xiàn)的問題。
為什么使用服務(wù)發(fā)現(xiàn)
設(shè)想下,我們寫了一些通過REST API或者Thrift API調(diào)用某個(gè)服務(wù)的代碼,為了發(fā)起這個(gè)請(qǐng)求,你的代碼需要知道服務(wù)實(shí)例的網(wǎng)絡(luò)地址(IP 地址和端口號(hào))。在傳統(tǒng)運(yùn)行在物理機(jī)器上的應(yīng)用中,某個(gè)服務(wù)實(shí)例的網(wǎng)絡(luò)地址一般是靜態(tài)的,比如,代碼可以從只會(huì)偶爾更新的配置文件中讀取網(wǎng)絡(luò)地址。
然而在現(xiàn)在流行的基于云平臺(tái)的微服務(wù)應(yīng)用中, 有更多如下圖所示的困難問題需要去解決:

服務(wù)實(shí)例需要?jiǎng)討B(tài)分配網(wǎng)絡(luò)地址,而且,一組服務(wù)實(shí)例可能會(huì)因?yàn)樽詣?dòng)擴(kuò)展、失敗或者升級(jí)發(fā)生動(dòng)態(tài)變化,因此 你的客戶端代碼應(yīng)該使用更加精細(xì)的服務(wù)發(fā)現(xiàn)機(jī)制。
有兩種主要的服務(wù)發(fā)現(xiàn)機(jī)制:客戶端發(fā)現(xiàn) 和 服務(wù)端發(fā)現(xiàn)。讓我們先看客戶端發(fā)現(xiàn)機(jī)制。
客戶端發(fā)現(xiàn)模式
當(dāng)我們使用 客戶端發(fā)現(xiàn)的時(shí)候,客戶端負(fù)責(zé)決定可用服務(wù)實(shí)例的網(wǎng)絡(luò)地址并且在集群中對(duì)請(qǐng)求負(fù)載均衡, 客戶端訪問服務(wù)登記表,也就是一個(gè)可用服務(wù)的數(shù)據(jù)庫(kù),然后客戶端使用一種負(fù)載均衡算法選擇一個(gè)可用的服務(wù)實(shí)例然后發(fā)起請(qǐng)求。
下圖展示了該結(jié)構(gòu)模式:

服務(wù)實(shí)例的網(wǎng)絡(luò)地址在服務(wù)啟動(dòng)的時(shí)候被登記到服務(wù)注冊(cè)表中 ,當(dāng)實(shí)例終止服務(wù)時(shí)從服務(wù)注冊(cè)表中移除。服務(wù)實(shí)例的注冊(cè)一般是通過心跳機(jī)制階段性的進(jìn)行刷新。
Netflix OSS 為客戶端發(fā)現(xiàn)機(jī)制提供了很多優(yōu)秀的例子。Netflix Eureka 實(shí)現(xiàn)了服務(wù)注冊(cè)表,它通過提供REST API來管理服務(wù)實(shí)例注冊(cè)以及可用實(shí)例的查詢。Netflix Ribbon 是一個(gè)與Eureka一起使用并在多個(gè)可用實(shí)例間對(duì)請(qǐng)求負(fù)載均衡的IPC客戶端。我們將在下面文章深入討論Eureka。
客戶端發(fā)現(xiàn)機(jī)制有諸多優(yōu)勢(shì)和劣勢(shì):該模式除了服務(wù)注冊(cè)表之外沒有其他的活動(dòng)部分了,相對(duì)來說還是簡(jiǎn)單直接的,而且,由于客戶端知道相關(guān)的可用服務(wù)實(shí)例,那么就可以使用更加智能的,特定于應(yīng)用的負(fù)載均衡機(jī)制,比如一致性哈希。一個(gè)明顯的缺點(diǎn)是它把客戶端與服務(wù)注冊(cè)表緊耦合了,你必須為每一種消費(fèi)服務(wù)的客戶端對(duì)應(yīng)的編程語言和框架實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)邏輯。
現(xiàn)在看完了客戶端發(fā)現(xiàn),再讓我們看下服務(wù)端發(fā)現(xiàn)吧。
服務(wù)端發(fā)現(xiàn)模式
服務(wù)發(fā)現(xiàn)的另一種模式就是服務(wù)端發(fā)現(xiàn)模式。下圖展示了該模式的結(jié)構(gòu):

客戶端通過一個(gè)負(fù)載均衡器向服務(wù)發(fā)送請(qǐng)求,負(fù)載均衡器查詢服務(wù)注冊(cè)表并把請(qǐng)求路由到一臺(tái)可用的服務(wù)實(shí)例上。和客戶端發(fā)現(xiàn)一樣,服務(wù)實(shí)例通過服務(wù)注冊(cè)表進(jìn)行服務(wù)的注冊(cè)和注銷。
AWS Elastic Load Balancer (ELB) 是服務(wù)端發(fā)現(xiàn)路由的一個(gè)示例。一個(gè)ELB通常對(duì)來自外部互聯(lián)網(wǎng)的請(qǐng)求進(jìn)行負(fù)載均衡,當(dāng)然,你也可以使用ELB對(duì)虛擬私有云(VPC)的內(nèi)部請(qǐng)求進(jìn)行負(fù)載均衡??蛻舳送ㄟ^DNS域名向ELB發(fā)起HTTP或者TCP請(qǐng)求,ELB將請(qǐng)求負(fù)載均衡到一系列注冊(cè)的Elastic Compute Cloud (EC2) 實(shí)例 或者EC2 Container Service (ECS) 的容器中,兩者并沒有分割的服務(wù)注冊(cè)表,EC2 實(shí)例和ECS容器都是通過ELB進(jìn)行注冊(cè)的。
類似NGINX PLUG和NGINX這些HTTP服務(wù)器和負(fù)載均衡器可以作為服務(wù)端發(fā)現(xiàn)負(fù)載均衡來使用。比如 這篇博客 就描述了使用Consul Template 動(dòng)態(tài)重配置NGINX反向代理,Consul Template是一種根據(jù)存儲(chǔ)在Consul 服務(wù)注冊(cè)表的配置數(shù)據(jù)階段性重新生成任意配置文件的工具 ,每當(dāng)文件發(fā)生變化時(shí),它將運(yùn)行任意的Shell 命令。在博客描述的例子中,Consul Template 生成用于配置反向代理的nginx.conf文件,然后運(yùn)行一個(gè)命令行告知NGINX重載配置。更復(fù)雜的實(shí)現(xiàn)可能使用 HTTP API or DNS動(dòng)態(tài)重配置NGINX Plus。
一些部署環(huán)境使用諸如Kubernetes 和Marathon在集群中的每個(gè)主機(jī)上運(yùn)行一個(gè)代理,這些代理扮演了服務(wù)端發(fā)現(xiàn)負(fù)載均衡的角色,代理可以根據(jù)主機(jī)IP地址和服務(wù)分配的端口號(hào)來路由客戶端請(qǐng)求,代理因此可以透明的把客戶端請(qǐng)求轉(zhuǎn)發(fā)到集群中某臺(tái)可用的服務(wù)實(shí)例上去。
服務(wù)端發(fā)現(xiàn)模式有一些優(yōu)勢(shì)也有一些劣勢(shì):一個(gè)巨大的優(yōu)勢(shì)是,服務(wù)發(fā)現(xiàn)的細(xì)節(jié)對(duì)客戶端來說是抽象的,客戶端僅需向負(fù)載均衡器發(fā)送請(qǐng)求即可。這種方式減少了為消費(fèi)服務(wù)的不同編程語言與框架實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)邏輯的麻煩。當(dāng)然,正如前面所述,一些部署環(huán)境已經(jīng)提供了該功能。這種模式也有一些劣勢(shì): 除非部署環(huán)境已經(jīng)提供了負(fù)載均衡器,否則這又是一個(gè)需要額外設(shè)置和管理的可高可用的系統(tǒng)組件。
服務(wù)注冊(cè)表
服務(wù)注冊(cè)表 是服務(wù)發(fā)現(xiàn)的關(guān)鍵部分,它是一個(gè)包含服務(wù)實(shí)例網(wǎng)絡(luò)地址的的數(shù)據(jù)庫(kù)。一個(gè)服務(wù)注冊(cè)表需要高可用和實(shí)時(shí)更新,客戶端可以緩存從服務(wù)注冊(cè)表獲取的網(wǎng)絡(luò)地址。然而,這樣的話緩存的信息最終會(huì)過期,客戶端不能再根據(jù)該信息發(fā)現(xiàn)服務(wù)實(shí)例。因此,服務(wù)注冊(cè)表對(duì)集群中的服務(wù)實(shí)例使用復(fù)制協(xié)議來維護(hù)一致性。
之前也提到 Netflix Eureka 是服務(wù)注冊(cè)表的好例子,它為服務(wù)實(shí)例的注冊(cè)與查詢提供了REST API:一個(gè)服務(wù)實(shí)例可以使用POST來注冊(cè)自己的網(wǎng)絡(luò)地址,它必須每30秒通過PUT去刷新,服務(wù)實(shí)例可以直接或者在服務(wù)實(shí)例注冊(cè)超時(shí)的時(shí)候使用DELETE刪除注冊(cè)表中的信息,正如你所料,客戶端可以使用HTTP GET獲取注冊(cè)實(shí)例的信息。
Netflix通過在每一個(gè)Amazon EC2 availability zone運(yùn)行一到多個(gè)Eureka服務(wù) 實(shí)現(xiàn)高可用 。每一個(gè)Eureka服務(wù)器運(yùn)行在一個(gè)關(guān)聯(lián) Elastic IP地址的 EC2 實(shí)例上。DNS TEXT記錄了Eureka集群的配置文件,配置文件映射availability zones到一組Eureka服務(wù)器可用的網(wǎng)絡(luò)地址。 Eureka 服務(wù)器啟動(dòng)的時(shí)候?qū)?huì)查詢DNS獲取Eureka集群的配置、查詢同等節(jié)點(diǎn)并為自己分配一個(gè)未被使用的Elastic IP地址。
Eureka clients – services和service clients,通過查詢DNS發(fā)現(xiàn)Eureka服務(wù)器的網(wǎng)絡(luò)地址??蛻舳烁鼉A向使用在同一availability zone中的Eureka服務(wù)器,當(dāng)然,如果該zone中沒有可用的網(wǎng)絡(luò)地址,它將使用另一zone中的。
其他的服務(wù)注冊(cè)表例子包括:
- etcd ,一個(gè)高可用、分布式、一致性、key-value 方式的存儲(chǔ),被用在分享配置和服務(wù)發(fā)現(xiàn)中。兩個(gè)著名的項(xiàng)目使用了它:Kubernetes 和 Cloud Foundry.
- consul ,一個(gè)發(fā)現(xiàn)和配置服務(wù)的工具,為客戶端注冊(cè)和發(fā)現(xiàn)服務(wù)提供了API,Consul還可以通過執(zhí)行健康檢查決定服務(wù)的可用性。
- Apache Zookeeper ,是一個(gè)廣泛使用、高性能的針對(duì)分布式應(yīng)用的協(xié)調(diào)服務(wù)。 Apache Zookeeper本來是Hadoop的子工程,現(xiàn)在已經(jīng)是頂級(jí)工程了。
正如前面所述,一些諸如Kubernetes、Marathon和AWS之類的應(yīng)用并沒有顯示的服務(wù)注冊(cè)表,相反,服務(wù)注冊(cè)表是架構(gòu)內(nèi)置的一部分。
我們已經(jīng)看過了服務(wù)注冊(cè)表的概念,現(xiàn)在我們看下服務(wù)實(shí)例是如何使用注冊(cè)表注冊(cè)的:
服務(wù)注冊(cè)選項(xiàng)
正如前面提到的那樣,服務(wù)實(shí)例必須使用服務(wù)注冊(cè)表來進(jìn)行服務(wù)的注冊(cè)和注銷,我們有幾種方式來處理服務(wù)的注冊(cè)和注銷,其中之一是服務(wù)實(shí)例自己注冊(cè)自己也就是self-registration 模式,另一種是系統(tǒng)的其他組件管理服務(wù)實(shí)例的注冊(cè),也就是 third-party registration 模式.。我們先看下self-registration模式:
Self-Registration模式
當(dāng)使用self-registration 模式,時(shí),服務(wù)實(shí)例自己負(fù)責(zé)通過服務(wù)注冊(cè)表對(duì)自己進(jìn)行注冊(cè)和注銷,另外如果有必要的話,服務(wù)實(shí)例可以通過發(fā)送心跳請(qǐng)求防止注冊(cè)過期,下圖展示了該模式的結(jié)構(gòu):

Netflix OSS Eureka client就是這種模式的一個(gè)例子,Eureka客戶端處理服務(wù)實(shí)例注冊(cè)和注銷的各個(gè)方面。Spring Cloud project實(shí)現(xiàn)了包括服務(wù)發(fā)現(xiàn)在內(nèi)的不同模式,使得自動(dòng)注冊(cè)服務(wù)實(shí)例到Eureka變的簡(jiǎn)單。你可以簡(jiǎn)單的在你的java配置類上添加@EnableEurekaClient注解即可。
self-registration模式有一些優(yōu)勢(shì)也有一些劣勢(shì):優(yōu)勢(shì)之一是它相對(duì)簡(jiǎn)單,而且不強(qiáng)制使用其他的系統(tǒng)組件。然而,一個(gè)很大的劣勢(shì)是 它使得服務(wù)實(shí)例和服務(wù)注冊(cè)表強(qiáng)耦合 ,你必須在每一個(gè)使用服務(wù)的客戶端編程語言和架構(gòu)代碼中實(shí)現(xiàn)注冊(cè)邏輯。
解綁服務(wù)和服務(wù)注冊(cè)表的另一替換方案是,使用third-party registration 模式。
Third-Party Registration模式
當(dāng)使用third-party registration 模式的時(shí)候,服務(wù)實(shí)例本身并不負(fù)責(zé)通過服務(wù)注冊(cè)表注冊(cè)自己,相反的,通過另一個(gè)被稱作 service registrar系統(tǒng)組件來處理注冊(cè)。 service registrar通過輪詢或者訂閱事件來檢測(cè)一些運(yùn)行實(shí)例的變化,當(dāng)它檢測(cè)到一個(gè)新的可用服務(wù)實(shí)例時(shí)就把該實(shí)例注冊(cè)到服務(wù)注冊(cè)表中去,service registrar還負(fù)責(zé)注銷已經(jīng)被終止的服務(wù)實(shí)例,下圖展示了該模式的架構(gòu):

service registrar其中一個(gè)例子是開源的Registrator 項(xiàng)目,它自動(dòng)的對(duì)部署到Docker 容器中的服務(wù)實(shí)例進(jìn)行注冊(cè)和注銷。 Registrator支持不同的服務(wù)注冊(cè)表,包括etcd和Consul。
service registrar的另一個(gè)例子是 NetflixOSS Prana,原本是為非JVM語言的服務(wù)所設(shè)計(jì),它像服務(wù)實(shí)例的跨斗一樣和服務(wù)實(shí)例一起運(yùn)行,Prana使用Netflix Eureka對(duì)服務(wù)進(jìn)行注冊(cè)和注銷。
service registrar是部署環(huán)境的內(nèi)置組件,EC2實(shí)例可以自動(dòng)擴(kuò)展組 并可使用ELB進(jìn)行服務(wù)注冊(cè)。Kubernetes 服務(wù)是自動(dòng)注冊(cè)的并能使其可以被發(fā)現(xiàn)。
third-party registration模式有一些優(yōu)勢(shì)也有一些劣勢(shì):主要優(yōu)勢(shì)是使得服務(wù)從服務(wù)注冊(cè)表中被解耦,你不必為開發(fā)者使用的每種開發(fā)語言和框架實(shí)現(xiàn)服務(wù)注冊(cè)的邏輯,相反,服務(wù)實(shí)例的注冊(cè)被一個(gè)專有服務(wù)以集中式的方式處理。
該模式的劣勢(shì)是,除非它被內(nèi)置在部署環(huán)境中,不然這又是一個(gè)需要被設(shè)置和管理的高可用系統(tǒng)組件。
總結(jié)
在一個(gè)微服務(wù)應(yīng)用中,一組運(yùn)行的服務(wù)實(shí)例是動(dòng)態(tài)變化的,實(shí)例有動(dòng)態(tài)分配的網(wǎng)絡(luò)地址,因此,為了使得客戶端能夠向服務(wù)發(fā)起請(qǐng)求,必須要要有服務(wù)發(fā)現(xiàn)機(jī)制。
服務(wù)發(fā)現(xiàn)的關(guān)鍵是服務(wù)注冊(cè)表,服務(wù)注冊(cè)表是可用服務(wù)實(shí)例的數(shù)據(jù)庫(kù),它提供了管理和查詢使用的API。服務(wù)實(shí)例使用這些管理API進(jìn)行服務(wù)的注冊(cè)和注銷,系統(tǒng)組件使用查詢API來發(fā)現(xiàn)可用的服務(wù)實(shí)例。
有兩種服務(wù)發(fā)現(xiàn)的模式:客戶端發(fā)現(xiàn)和服務(wù)端發(fā)現(xiàn)。在使用客戶端發(fā)現(xiàn)模式的系統(tǒng)中,客戶端直接查詢服務(wù)注冊(cè)表,選擇一個(gè)可用的實(shí)例并發(fā)起請(qǐng)求,在一個(gè)使用服務(wù)端發(fā)現(xiàn)模式的系統(tǒng)中,客戶端通過路由發(fā)起請(qǐng)求,路由會(huì)查詢服務(wù)注冊(cè)表并把請(qǐng)求轉(zhuǎn)發(fā)到可用的服務(wù)實(shí)例上。
對(duì)服務(wù)實(shí)例來講有兩種方式可以對(duì)服務(wù)注冊(cè)表進(jìn)行注冊(cè)和注銷,一種是服務(wù)實(shí)例本身通過服務(wù)注冊(cè)表來注冊(cè)自己,也就是self-registration模式,另一種則是第三方系統(tǒng)組件代表實(shí)例來處理服務(wù)的注冊(cè)和注銷,也就是third-party registration模式。
在一些部署環(huán)境中,你需要使用諸如Netflix Eureka、etcd, 或者Apache Zookeeper這樣的服務(wù)注冊(cè)表來設(shè)置你自己的服務(wù)發(fā)現(xiàn)架構(gòu)。在另一些部署環(huán)境中,服務(wù)發(fā)現(xiàn)則是內(nèi)置組件,比如Kubernetes 和 Marathon用來處理服務(wù)注冊(cè)和注銷,他們同樣也在集群的每一個(gè)主機(jī)上運(yùn)行一個(gè)代理來扮演服務(wù)端發(fā)現(xiàn)路由的角色。
一些諸如NGINX的HTTP反向代理和負(fù)載均衡器 可以用作服務(wù)端發(fā)現(xiàn)負(fù)載均衡器使用,服務(wù)注冊(cè)表可以向NGINX推送路由信息并調(diào)用優(yōu)雅的配置更新,比如,你可以使用Consul Template。 NGINX Plus 支持額外的動(dòng)態(tài)重配置機(jī)制 :它可以使用DNS從服務(wù)注冊(cè)表 拉取服務(wù)實(shí)例的信息,并且為遠(yuǎn)程重配置提供API。
在該系列之后的文章中我們將繼續(xù)挖掘微服務(wù)的各個(gè)方面。