面試不再慌!跟著老司機(jī)吃透Spring Cloud
原創(chuàng)【51CTO.com原創(chuàng)稿件】最近和朋友聊天,提到他前幾天面試的時(shí)候被問(wèn)到:“能否描述一下Spring Cloud?”他當(dāng)場(chǎng)就懵了,不知道從何說(shuō)起。
圖片來(lái)自 Unsplash
是啊,Spring Cloud 是知名的微服務(wù)架構(gòu),包含了很多組件,每個(gè)組件又有各自的分工。
怎么才能理解 Spring Cloud 架構(gòu)并且說(shuō)清楚它到底做了些什么呢?我們今天一起來(lái)看一下。
從一個(gè)例子開始
對(duì)于這樣的“大”問(wèn)題,通常需要拆解成小問(wèn)題來(lái)回答。要說(shuō)明 Spring Cloud 做了什么,就要說(shuō)清楚它包含的組件都做了些什么?
如果一個(gè)個(gè)把組件羅列出來(lái),似乎太過(guò)獨(dú)立,沒(méi)有關(guān)聯(lián)性,缺少邏輯感。我們就從一個(gè)簡(jiǎn)單的例子開始,把這些組件像串珍珠一樣串起來(lái)。
假設(shè)有一個(gè)項(xiàng)目,這個(gè)項(xiàng)目有兩個(gè)服務(wù),分別是“A”和“B”:
- “A”和“B”的關(guān)系是,“A”調(diào)用“B”。
- 然后,有一個(gè)客戶端“C”調(diào)用“A”。
客戶端“C”調(diào)用服務(wù)“A”,服務(wù)“A”調(diào)用服務(wù)“B”
Eureka 服務(wù)之間互相認(rèn)識(shí)
在服務(wù)端我們已經(jīng)有了兩個(gè)服務(wù),“A”和“B”。他們的關(guān)系是“A”調(diào)用“B”,“B”被“A”調(diào)用。
當(dāng)只有兩個(gè)服務(wù)的時(shí)候我們是知道這種關(guān)系,并且可以把這種關(guān)系記錄下來(lái)的,但是如果服務(wù)一多,我們?nèi)绾斡涗涍@種關(guān)系呢?
于是,Eureka 就登場(chǎng)了,它負(fù)責(zé)“服務(wù)注冊(cè),服務(wù)發(fā)現(xiàn)”的工作。Eureka 分成 Eureka Server 和 Eureka Client。
每個(gè)微服務(wù)架構(gòu)都會(huì)有一個(gè)或者多個(gè) Eureka Server 用來(lái)保存注冊(cè)服務(wù)的信息。
每個(gè)服務(wù)都會(huì)包含一個(gè)Eureka Client,其中會(huì)配置Eureka Server的信息,這樣當(dāng)服務(wù)啟動(dòng)的時(shí)候就能夠把自己注冊(cè)到 Eureka Server 中去了。
同時(shí)每個(gè)服務(wù)也可以通過(guò) Eureka Client 從 Eureka Server 中獲取其他服務(wù)的信息(Get Registry)。
“A”服務(wù)與“B”服務(wù)的調(diào)用關(guān)系
“A”服務(wù)和“B”服務(wù)首先通過(guò)自身集成的 Eureka Client 到 Eureka Server 上注冊(cè)自身的信息,包括:服務(wù)名,地址,端口號(hào)等等。
注冊(cè)完畢以后,“A”服務(wù)通過(guò) Eureka Client 從 Eureka Server 獲取(Get Registry)服務(wù)“B”的信息。
由于,“A”服務(wù)調(diào)用“B”服務(wù),所以“A”服務(wù)稱之為“消費(fèi)者”,“B”服務(wù)稱之為“生產(chǎn)者”。
Feign 服務(wù)之間信息傳遞
既然“A”“B”兩個(gè)服務(wù)互相認(rèn)識(shí)了,接下來(lái)就要輪到“A”服務(wù)調(diào)用“B”服務(wù)了。
由于兩個(gè)是單獨(dú)的服務(wù),并且兩個(gè)服務(wù)都在一個(gè)網(wǎng)絡(luò)內(nèi),通常會(huì)通過(guò) HTTP 請(qǐng)求進(jìn)行調(diào)用。
傳統(tǒng)的做法是,“A”服務(wù)寫好請(qǐng)求的消息,序列化成二進(jìn)制的串傳遞給“B”服務(wù),“B”服務(wù)收到消息以后反序列化消息進(jìn)行解析,接著以同樣的方式應(yīng)答“A”服務(wù)。
從傳統(tǒng)意義上完成這些代碼需要大量的工作,而且需要考慮很多編碼上面的問(wèn)題。為了簡(jiǎn)化上面的過(guò)程,F(xiàn)eign 組件就誕生了,它方便了服務(wù)之間的調(diào)用。
引入 Feign 了以后,在“A”服務(wù)中只需要填寫簡(jiǎn)單的 URL,參數(shù),請(qǐng)求方式,就可以調(diào)用“B”服務(wù)了。調(diào)用“B”服務(wù)就好像調(diào)用本地的一個(gè)方法一樣簡(jiǎn)單。
通過(guò) Feign 調(diào)用服務(wù)的代碼片段
我們需要做的就是在“A”服務(wù)(消費(fèi)者)中建立一個(gè) Feign Client,填寫我們需要調(diào)用的 URL,參數(shù)和方式就可以了,其他的事情就交給 Feign 來(lái)完成了。

Feign Client 工作流
從上圖可以看出,“消費(fèi)者”開始只需要提供“生產(chǎn)者”的 URL,參數(shù)等信息。
Feign Client 會(huì)根據(jù)這些信息生成對(duì)應(yīng)的 HTTP 請(qǐng)求頭和報(bào)文,然后發(fā)送給生產(chǎn)者。
生產(chǎn)者返回信息以后,F(xiàn)eign Client 同樣會(huì)返回“消費(fèi)者”能夠讀懂的 JavaBean 的信息。
Ribbon 搞定負(fù)載均衡
好了,現(xiàn)在“A”“B”服務(wù)互相認(rèn)識(shí)了,并且“A”服務(wù)可以調(diào)用“B”服務(wù)了。
假設(shè)“B”服務(wù)的業(yè)務(wù)量增大,一個(gè)“B”服務(wù)無(wú)法滿足現(xiàn)在的要求,另外又復(fù)制了兩個(gè)“B”服務(wù),連同原來(lái)的一個(gè)“B”服務(wù),現(xiàn)在一共有三個(gè)“B”服務(wù)。
雖然三個(gè)提供的服務(wù)都是一樣的,但是“A”服務(wù)應(yīng)該調(diào)用哪個(gè)“B”服務(wù)的復(fù)制呢?
這時(shí) Ribbon 就登場(chǎng)了,它用來(lái)做負(fù)載均衡。“A”服務(wù)無(wú)需知道調(diào)用三個(gè)復(fù)制中的哪一個(gè),它只用告訴 Ribbon 我要調(diào)用“B”服務(wù),Ribbon 會(huì)根據(jù)策略去調(diào)用三個(gè)復(fù)制中的某一個(gè)。
Ribbon 充當(dāng)負(fù)載均衡器的角色
在 Ribbon 的幾種負(fù)載均衡策略中,隨機(jī)策略是用的比較多的。例如:“B1”,“B2”,“B3”分別是“B”服務(wù)的三個(gè)復(fù)制。
“A”服務(wù)第一次,調(diào)用的是“B1”服務(wù),根據(jù)隨機(jī)策略,第二次就訪問(wèn)“B2”服務(wù),第三次訪問(wèn)“B3”服務(wù),第四次又訪問(wèn)“B1”服務(wù),依次類推,循環(huán)往復(fù)。
下面列出其他幾個(gè)服務(wù)僅供參考,篇幅有限不做贅述:
- 隨機(jī)(Random)
- 輪詢(RoundRobin)
- 一致性哈希(ConsistentHash)
- 哈希(Hash)
- 加權(quán)(Weighted)
Hystrix 服務(wù)出現(xiàn)故障
現(xiàn)在“A”服務(wù)知道如何調(diào)用“B1”,“B2”,“B3”三個(gè)“B”服務(wù)的復(fù)制了。
假如“B2”服務(wù)出現(xiàn)故障,“A”服務(wù)還可以訪問(wèn)它嗎?為了避免單個(gè)服務(wù)的故障影響到其他服務(wù),Hystrix 就應(yīng)運(yùn)而生了。
調(diào)用“B1”服務(wù)失敗
Hystrix 俗稱熔斷器(斷路器),當(dāng)服務(wù)不可用或者出現(xiàn)故障時(shí),它提供了響應(yīng)的處理機(jī)制。
在微服務(wù)架構(gòu)中,每個(gè)服務(wù)都有可能依賴其他服務(wù),也有可能多個(gè)服務(wù)同時(shí)依賴一個(gè)服務(wù),又或者存在服務(wù)之間的連環(huán)依賴(A 依賴 B,B 依賴 D)。
一旦被依賴的服務(wù)出現(xiàn)故障,Hystrix 可以通過(guò)預(yù)設(shè)的處理機(jī)制,調(diào)整服務(wù)的響應(yīng)。例如:返回錯(cuò)誤信息,用緩存或者兜底信息替代服務(wù)返回信息。
單個(gè)服務(wù)依賴多個(gè)服務(wù),依賴服務(wù)中有一個(gè)出現(xiàn)問(wèn)題,對(duì)整體產(chǎn)生影響。
多個(gè)服務(wù)依賴單個(gè)服務(wù),單個(gè)服務(wù)出現(xiàn)故障,影響多個(gè)服務(wù)。
應(yīng)用 Hystrix 只需要兩步:
第一步,在“A”服務(wù)(消費(fèi)者)上定義,調(diào)用“B”服務(wù)(生產(chǎn)者)出現(xiàn)故障時(shí)的處理方法。
調(diào)用“B”服務(wù)故障處理的方法,代碼片段。
第二步,在“A”服務(wù)(消費(fèi)者)調(diào)用“B”服務(wù)的方法的 Annotation 上面標(biāo)注調(diào)用失敗需要執(zhí)行的“第一步”的這個(gè)方法。
聲明調(diào)用失敗方法,代碼片段
在“A”服務(wù)中加入 Hystrix
Zuul 如何訪問(wèn)到微服務(wù)
上面把服務(wù)端的事情說(shuō)的差不多了,如果“C”客戶端需要訪問(wèn)“A”服務(wù),系統(tǒng)通過(guò)什么方式告訴它哪個(gè)服務(wù)是“A”呢?
實(shí)際上這里缺少一個(gè)網(wǎng)關(guān),把“C”客戶端與“A”服務(wù)鏈接起來(lái)的網(wǎng)關(guān)。
Zuul 就是這個(gè)網(wǎng)關(guān),它的責(zé)任是過(guò)濾和路由。
Zuul 是鏈接客戶端和服務(wù)端的網(wǎng)關(guān)
還記得上面提到的 Eureka 服務(wù)注冊(cè)和服務(wù)發(fā)現(xiàn)嗎?Zuul 可以和 Eureka 一起合作,完成服務(wù)路由的工作。
首先,“A”服務(wù)在 Eureka 進(jìn)行注冊(cè),然后“C”客戶端向 Zuul 發(fā)起請(qǐng)求,訪問(wèn)“A”服務(wù)。Zuul 向 Eureka 獲取“A”服務(wù)的地址,之后訪問(wèn)“A”服務(wù)。
Zuul 與 Eureka 協(xié)同工作
Zuul 除了可以做路由,還可以做過(guò)濾器,針對(duì)權(quán)限驗(yàn)證,金絲雀測(cè)試都可以用到它。這里簡(jiǎn)單說(shuō)說(shuō) Zuul 內(nèi)部的運(yùn)行機(jī)制。
Zuul 收到 HTTP 請(qǐng)求以后,會(huì)通知 Zuul Servlet 處理,與此同時(shí)會(huì)生成一個(gè) Request Context 用來(lái)記錄請(qǐng)求的上下文信息,它會(huì)一直保持直到路由結(jié)束。
Zuul Filter Runner 接到 Zuul Servlet 的通知以后,會(huì)從 Request Context 中取請(qǐng)求的信息,并且交給 Filter Processer 處理,它會(huì)維護(hù)一套過(guò)濾和路由的規(guī)則,根據(jù)這些規(guī)則將請(qǐng)求發(fā)送到目標(biāo)的服務(wù)。
Zuul 內(nèi)部工作原理圖
Spring Cloud 微服務(wù)架構(gòu)總結(jié)
說(shuō)完了上面這些是不是對(duì) Spring Cloud 理解更加深了。讓我們來(lái)回顧一下,Spring Cloud 是一個(gè)微服務(wù)架構(gòu),它為微服務(wù)開發(fā)提供了豐富的組件。
其中比較重要的五大組件分別是:
- Eureka:服務(wù)發(fā)現(xiàn),服務(wù)注冊(cè)。
- Feign:服務(wù)調(diào)用請(qǐng)求。
- Ribbon:服務(wù)之間負(fù)載均衡。
- Hystrix:熔斷器。
- Zuul:服務(wù)網(wǎng)關(guān)。
如果,用我們上面 ABC 的例子來(lái)記憶就是,A 調(diào)用 B(Eureka),A 發(fā)送請(qǐng)求(Feign),B 做橫向擴(kuò)展以后,A 通過(guò)(Ribbon)找到 B,B 出現(xiàn)問(wèn)題 A 通過(guò)熔斷機(jī)制(Hystrix)保證服務(wù)調(diào)用正常,C 通過(guò) Zuul 找到 A。
用一張大圖來(lái)總結(jié)一下:
通過(guò) ABC 理解 Spring Cloud 的五大組件
還可以把整個(gè)流程總結(jié)一下,客戶端請(qǐng)求→Zuul→Eureka 獲取服務(wù)→Feign 通信→Ribbon 負(fù)載均衡→Hystrix 熔斷:
- 用戶請(qǐng)求會(huì)最先發(fā)送給 Zuul,Zuul 是用來(lái)做 API 網(wǎng)關(guān)的。同時(shí)它也可以作為過(guò)濾器。
- 微服務(wù)的注冊(cè)操作需要通過(guò) Eureka,作為服務(wù)發(fā)現(xiàn)和注冊(cè)中心,一方面記錄服務(wù)的注冊(cè)以及健康情況,一方面會(huì)協(xié)同 Zuul 做好服務(wù)訪問(wèn)的工作。
- 微服務(wù)之間通訊,需要把數(shù)據(jù)打包發(fā)送,接受以后也需要解包讀取信息。這里可以使用 Feign 作為服務(wù)通訊的組件,配合 Ribbon 完成通信工作。
- Robbin,其負(fù)責(zé)微服務(wù)集群的負(fù)載均衡工作。
- 服務(wù)出現(xiàn)故障,例如:業(yè)務(wù)異常,網(wǎng)絡(luò)異常等等。需要通過(guò)斷路器 Hystrix 來(lái)實(shí)現(xiàn)具體的處理操作,比如通知注冊(cè)中心服務(wù)異常,比如對(duì)服務(wù)進(jìn)行降級(jí)處理。
這個(gè)時(shí)候服務(wù)注冊(cè)發(fā)現(xiàn)中心會(huì)標(biāo)記服務(wù)異常,再有請(qǐng)求過(guò)來(lái)就不會(huì)發(fā)送到有異常的服務(wù)上去了。
同時(shí)服務(wù)發(fā)現(xiàn)注冊(cè)中心也會(huì)定期檢查服務(wù)的狀態(tài),一旦服務(wù)恢復(fù)狀態(tài)又把其放到訪問(wèn)隊(duì)列中。
作者:崔皓
簡(jiǎn)介:十六年開發(fā)和架構(gòu)經(jīng)驗(yàn),曾擔(dān)任過(guò)惠普武漢交付中心技術(shù)專家,需求分析師,項(xiàng)目經(jīng)理,后在創(chuàng)業(yè)公司擔(dān)任技術(shù)/產(chǎn)品經(jīng)理。善于學(xué)習(xí),樂(lè)于分享。目前專注于技術(shù)架構(gòu)與研發(fā)管理。
【51CTO原創(chuàng)稿件,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文作者和出處為51CTO.com】