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

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