天天看點

Spring Cloud Zuul 快速入門

為什麼要有服務網關:

我們都知道在微服務架構中,系統會被拆分為很多個微服務。那麼作為用戶端要如何去調用這麼多的微服務呢?難道要一個個的去調用嗎?很顯然這是不太實際的,我們需要有一個統一的接口與這些微服務打交道,這就是我們需要服務網關的原因。

我們已經知道,在微服務架構中,不同的微服務可以有不同的網絡位址,各個微服務之間通過互相調用完成使用者請求,用戶端可能通過調用N個微服務的接口完成一個使用者請求。比如:使用者檢視一個商品的資訊,它可能包含商品基本資訊、價格資訊、評論資訊、折扣資訊、庫存資訊等等,而這些資訊擷取則來源于不同的微服務,諸如産品系統、價格系統、評論系統、促銷系統、庫存系統等等,那麼要完成使用者資訊檢視則需要調用多個微服務,這樣會帶來幾個問題:

用戶端多次請求不同的微服務,增加用戶端代碼或配置編寫的複雜性

認證繁雜,通路每個服務都要進行一次認證

每個服務都通過http通路,導緻http請求增加,效率不高拖慢系統性能

多個服務存在跨域請求問題,處理起來比較複雜

如下圖所示:

Spring Cloud Zuul 快速入門

我們該如何解決這些問題呢?我們可以嘗試想一下,不要讓前端直接知道背景諸多微服務的存在,我們的系統本身就是從業務領域的層次上進行劃分,形成多個微服務,這是背景的處理方式。對于前台而言,背景應該仍然類似于單體應用一樣,一次請求即可,于是我們可以在用戶端和服務端之間增加一個API網關,所有的外部請求先通過這個微服務網關。它隻需跟網關進行互動,而由網關進行各個微服務的調用。

這樣的話,我們就可以解決上面提到的問題,同時開發就可以得到相應的簡化,還可以有如下優點:

減少用戶端與微服務之間的調用次數,提高效率

便于監控,可在網關中監控資料,可以做統一切面任務處理

便于認證,隻需要在網關進行認證即可,無需每個微服務都進行認證

降低用戶端調用服務端的複雜度

這裡可以聯想到一個概念,面向對象設計中的門面模式,即對用戶端隐藏細節,API網關也是類似的東西,隻不過叫法不同而已。它是系統的入口,封裝了應用程式的内部結構,為用戶端提供統一服務,一些與業務本身功能無關的公共邏輯可以在這裡實作,諸如認證、鑒權、監控、緩存、負載均衡、流量管控、路由轉發等等。示意圖:

Spring Cloud Zuul 快速入門

總結一下,服務網關大概就是四個功能:統一接入、流量管控、協定适配、安全維護。而在目前的網關解決方案裡,有Nginx+ Lua、Kong、Tyk以及Spring Cloud Zuul等等。這裡以Zuul為例進行說明,它是Netflix公司開源的一個API網關元件,Spring Cloud對其進行二次封裝做到開箱即用。同時,Zuul還可以與Spring Cloud中的Eureka、Ribbon、Hystrix等元件配合使用。

可以說,Zuul實作了兩個功能,路由轉發和過濾器:

路由轉發:接受請求,轉發到後端服務

過濾器:提供一系列過濾器完成權限、日志、限流等切面任務。

可以說路由+過濾器=Zuul

服務網關的要素:

網關作為唯一的入口,是以穩定性和高可用是跑不了了

以及具備良好的并發性能

安全性,確定服務不被惡意通路

擴充性,網關容易成為吞吐量的瓶頸,是以需要便于擴充

Zuul的四種過濾器API:

前置(Pre)

路由(Route)

後置(Post)

錯誤(Error)

zuul前後置過濾器的典型應用場景:

限流

鑒權

參數校驗調整

統計

日志

Zuul的核心是一系列過濾器,開發者通過實作過濾器接口,可以做大量切面任務,即AOP思想的應用。Zuul的過濾器之間沒有直接的互相通信,而是通過本地ThreadLocal變量進行資料傳遞的。Zuul架構圖:

Spring Cloud Zuul 快速入門

在Zuul裡,一個請求的生命周期:

Spring Cloud Zuul 快速入門

本小節我們來學習如何使用服務網關,也就是Spring Cloud Zuul這個元件,首先建立一個項目,選擇如下子產品:

Spring Cloud Zuul 快速入門

pom.xml配置的依賴如下:

項目建立好後,将application.properties改為bootstrap.yml,編輯内容如下:

注:我這裡使用了配置中心,若對此不熟悉的話,可以參考我另一篇文章:Spring Cloud Config - 統一配置中心

在啟動類中,加上<code>@EnableZuulProxy</code>注解,代碼如下:

完成以上配置後啟動這個項目,我這裡項目啟動是正常的。然後我們來通過這個網關通路一下商品服務中擷取商品清單的接口。如下:

Spring Cloud Zuul 快速入門

通路位址說明:

該zuul項目跑在8951端口上

第一個<code>/product</code>是需要通路的服務的名稱

後面跟的<code>/buyer/product/list</code>是商品服務中擷取商品清單的接口位址

隻要是在eureka上注冊的服務都能夠通過zuul進行轉發,例如我通過zuul來通路config的配置檔案:

Spring Cloud Zuul 快速入門

如上,可以看到,報錯了,網關逾時。這是因為預設情況下,zuul的熔斷機制逾時時間是2秒,當一個服務響應的時間較長就會報網關逾時錯誤。

我們在配置檔案中,加上如下逾時時間的配置即可:

Spring Cloud Zuul 快速入門

ribbon.ReadTimeout, ribbon.SocketTimeout這兩個就是ribbon逾時時間設定,當在yml寫時,應該是沒有提示的,給人的感覺好像是不是這麼配的一樣,其實不用管它,直接配上就生效了。

還有zuul.host.connect-timeout-millis, zuul.host.socket-timeout-millis這兩個配置,這兩個和上面的ribbon都是配逾時的。差別在于,如果路由方式是serviceId的方式,那麼ribbon的生效,如果是url的方式,則zuul.host開頭的生效。(此處重要!使用serviceId路由和url路由是不一樣的逾時政策)

如果你在zuul配置了熔斷fallback的話,熔斷逾時也要配置,即hystrix那段配置。不然如果你配置的ribbon逾時時間大于熔斷的逾時,那麼會先走熔斷,相當于你配的ribbon逾時就不生效了。

現在重新開機項目,再次通路之前的位址,就不會出現網關逾時的錯誤了:

Spring Cloud Zuul 快速入門

之前我們通路的都是GET類型的接口,我們來看看POST類型的是否能夠正常通路。如下:

Spring Cloud Zuul 快速入門

每次請求某個服務的接口,都需要帶上這個服務的名稱。有沒有辦法可以自定義這個規則呢?答案是有的,在配置檔案中,增加路由的自定義配置:

說明:

myProduct 自定義的字首

path 比對的位址

product 路由到哪個服務

重新開機項目,測試如下:

Spring Cloud Zuul 快速入門

在項目啟動的時候,我們也可以在控制台中檢視到zuul所有的路由規則:

Spring Cloud Zuul 快速入門

如果我們有些服務的接口不希望對外暴露,隻希望在服務間調用,那麼就可以在配置檔案中,增加路由排除的配置。例如我不希望<code>listForOrder</code>被外部通路,則在配置檔案中,增加如下配置即可:

重新開機項目,這時通路就會報404了。如下:

Spring Cloud Zuul 快速入門

還可以使用通配符進行比對。如下示例:

我們在web開發中,經常會利用到cookie來儲存使用者的登入辨別。但我們使用了zuul元件後,預設情況下,cookie是無法直接傳遞給服務的,因為cookie預設被列為敏感的headers。是以我們需要在配置檔案中,将sensitiveHeaders的值置空。如下:

我們每次配置路由資訊都需要重新開機項目,顯得很麻煩,線上上環境也不能這樣随便重新開機項目。是以我們得實作動态路由的功能,實作動态路由其實就利用一下我們之前實作的動态重新整理配置檔案的功能即可。首先把Zuul路由相關的配置剪切到git上,如下:

Spring Cloud Zuul 快速入門

在pom.xml檔案中,增加如下依賴項:

然後在bootstrap.yml中,增加rabbitmq的配置。如下:

最後在項目中建立一個config包,在該包中建立一個ZuulConfig配置類,用于加載配置檔案中的配置。代碼如下:

完成以上配置後,重新開機項目,即可實作動态路由了,例如我現在把myProduct改成yourProduct,如下:

Spring Cloud Zuul 快速入門

此時無需重新開機項目,通路新的位址即可。如下:

Spring Cloud Zuul 快速入門

因為Zuul也屬于一個微服務,是以我們将多個Zuul節點注冊到Eureka Server即可實作Zuul的高可用性

将Nginx和Zuul “混搭”,利用nginx做負載均衡,轉發到多個Zuul上

繼續閱讀