原文首發地址:Zuul,據說SpringCloud不許備要我了,但是爲何面試還要每天問我?html
Spring Cloud 微服務精彩系列前端
- 阿里面試官問我:到底知不知道什麼是Eureka,此次,我沒沉默
- 萬字詳解Ribbon架構,針對面試高頻題多角度細說Ribbon
- 什麼是Hystrix,阿里技術最終面,遺憾的倒在Hystrix面前!
- 2萬字好文全方位深刻學習SpringCloud Fegin,面試不在彷徨
- Zuul,據說SpringCloud不許備要我了,但是爲何面試還要每天問我?
什麼是Api網關?
咱們能夠將API網關看作是一個反向路由器,它讓咱們不須要關注內部的細節,API網關統一服務入口,可方便實現對平臺衆多服務接口進行管控。java
Api網關全部的調用者的請求都接收過來,而後經過指定的路由機制轉發到特定的服務實例上。web
咱們能夠將網關看作成一組過濾器組成的集合,有一些與業務無關的公共邏輯能夠抽象到網關中實現,如安全認證、限流熔斷、日誌監控,黑白名單,緩存,壓力測試、灰度發佈等。面試
爲何須要網關?
API網關給企業帶來的好處
(1)網關層對外部和內部進行了隔離,保障了後臺服務的安全性。spring
(2)對外訪問控制由網絡層面轉換成了運維層面,減小變動的流程和錯誤成本。json
(3)減小客戶端與服務的耦合,服務能夠獨立運行,並經過網關層來作映射。後端
(4)經過網關層聚合,減小外部訪問的頻次,提高訪問效率。api
(5)節約後端服務開發成本,減小上線風險。瀏覽器
(6)爲服務熔斷,灰度發佈,線上測試提供簡單方案。
(7)便於進行應用層面的擴展。
上面展現的是API網關的核心架構圖,在圖中的核心功能模塊中咱們能夠看到API網關內部幫我實現了這麼的多功能模塊,看到API網關幫咱們實現了這麼多的核心功能,我想你們應該都知道爲何須要使用網關了吧。
什麼是Zuul?
Zuul做爲微服務系統的網關組件,是從設備和網站到Netflix流應用程序後端的全部請求的前門。zuul做爲整個應用的流量入口,接收全部的請求,如app、網頁等,而且將不一樣的請求轉發至不一樣的處理微服務模塊。做爲邊緣服務應用程序,Zuul旨在實現動態路由,監控,彈性和安全性。
Zuul 主要應用場景
1.黑白名單:實現經過IP地址控制禁止訪問網關功能,此功能是應用層面控制實現,再往前也能夠經過網絡傳輸方面進行控制訪問。
2.日誌:實現訪問日誌的記錄,可用於分析訪問、處理性能指標,同時將分析結果支持其餘模塊功能應用。
3.協議適配:實現通訊協議校驗、適配轉換的功能。
4.身份認證:負責網關訪問身份認證驗證,此模塊與「訪問認證中心」通訊,實際認證業務邏輯交移「訪問認證中心」處理。
5.計流限流:實現微服務訪問流量計算,基於流量計算分析進行限流,能夠定義多種限流規則。
6.路由:路由是API網關很核心的模塊功能,此模塊實現根據請求,鎖定目標微服務並將請求進行轉發。此模塊須要與「服務發佈管理中心」通訊。「服務發佈管理中心」實現微服務發佈註冊管理功能,與其通訊得到目標微服務信息。
Zuul核心架構剖析
Zuul網關架構圖
-
Zuul Servlet:zuul的servlet容器
-
Zuul Filter Runner:zuul執行filter的處理器
-
Pre routing Filter:zuul請求的前置過濾器
-
Routing Filter:zuul請求的路由放行過濾器
-
Post routing Filter:zuul請求的後置過濾器
-
Request Context:zuul servlet的上下文
-
Filter Loader:filter加載器
-
Filter File Manager:filter內容管理器
-
Filter Directory:filter過濾器存放路徑
-
Filter Publisher:發佈filter的處理類
-
Filter Persister:持久化filter的處理類
-
Filter Poller:輪詢Persister中的filter並將新filter推送至Filter Directory
Zuul請求處理生命週期
Zuul請求的生命週期如圖所示,該圖詳細描述了各類類型的過濾器的執行順序。
-
http發送請求到zuul網關
-
zuul網關首先通過pre filter;
-
驗證經過後進入routing filter,接着將請求轉發給遠程服務,遠程服務執行完返回結果,若是出錯,則執行error filter;
-
繼續往下執行post filter;
-
最後返回響應給http 客戶端。
Zuul的實戰演練
第一步,新建一個模塊ZuulServer
引入zuul和eureka client的依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
第二步,在Spring boot的主類上增長註解@EnableZuulProxy
package com.javaer.study.zuulserveer;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * @Author 公衆號【Java學習部落】 * * 關注公衆號獲取4000G精品視頻資源 * * 更有更多學習Java後端、中間件,大數據、微服務、分佈式、大數據等學習乾貨 **/@SpringBootApplication@EnableZuulProxypublic class ZuulserveerApplication { public static void main(String[] args) { SpringApplication.run(ZuulserveerApplication.class, args); }}
第三步,添加application.yml配置
server: port: 8888spring: application: name: zuul-serverzuul: routes: #標識你服務的名字,這裏能夠本身定義,通常方便和規範來說仍是跟本身服務的名字同樣 order-service: #服務映射的路徑,經過這路徑就能夠從外部訪問你的服務了,目的是爲了避免爆露你機器的IP, #面向服務的路由了,給你選一個可用的出來, #這裏zuul是自動依賴hystrix,ribbon的,不是面向單機 path: /order-service/** #這裏必定要是你Eureka註冊中心的服務的名稱,是因此這裏配置serviceId由於跟eureka結合了, #若是單獨使用zuul,那麼就必須寫本身機器的IP了, #如url:http://localhost:8080/ 這樣的很差就是寫死IP了, #萬一這IP掛了,這高可用性,服務註冊那套東西就用不起來了 serviceId: order-serviceeureka: #客戶端 client: #註冊中心地址 service-url: defaultZone: http://localhost:8761/eureka/
代碼部分完成。
接下來啓動,首先啓動Eureka server,而後啓動以前文章中建立的OrderService,最後啓動剛剛建立的ZuulServer。
而後在瀏覽器請求http://localhost:8888/order-service/test 便可。
Zuul路由詳解
Zuul的路由包含兩種路由
1. 傳統路由
所謂的傳統路由配置方式就是在不依賴於服務發現機制的狀況下,經過在配置文件中具體指定每一個路由表達式與服務實例的映射關係來實現 API 網關對外不請求的路由。
2. 面向服務路由。
傳統路由的配置方式須要運維人員花費大量的時間來維護各個路由 path 與 url 的關係。爲了解決這個問題,Spring Cloud Zuul 實現了與 Spring Cloud Eureka 的無縫整合,咱們可讓路由的 path 不是映射具體的 url ,而是讓它映射到某個具體的服務,而具體的 url 則交給 Eureka 的服務發現機制去自動維護。這類的路由便稱爲面向服務的路由。
傳統路由配置
單實例配置
單實例的路由轉發經過 zuul.routes.<route>.path 與 zuul.routes.<route>.url 參數對的方式進行配置。
好比下面配置實現了對符合 /zuul-service/** 規則的請求路徑轉發到 http://localhost:8888/ 地址的路由規則
zuul.routes.zuul-service.path=/zuul-service/**zuul.routes.zuul-service.url=http://localhost:8888/
多實例配置
多實例的路由轉發經過 zuul.routes.<route>.path 與 zuul.routes.<route>.service-id 參數對的方式進行配置,其中 service-id 是由用戶手工命名的服務名稱,配合 ribbon.listOfServers 參數實現服務與實例的維護:
好比下面配置實現了對符合 /my-service/** 規則的請求路徑轉發到 http://localhost:8888/ 和 http://localhost:9999/ 兩個實例地址的路由規則。
zuul.routes.zuul-service.path=/zuul-service/**zuul.routes.zuul-service.service-id=zuul-servicezuul-service.ribbon.listOfServers=http://localhost:8888/,http://localhost:9999/
面向服務路由配置
默認規則
當咱們爲 Spring Cloud Zuul 構建的 API 網關服務引入 Spring Cloud Eureka 以後,它會爲 Eureka 中的每一個服務都自動建立一個默認路由規則:使用服務名做爲 path 請求前綴。
好比 http://192.168.0.128:8888/order-service/order這個請求就會直接轉發到 ORDER-SERVICE 服務實例上。
關閉默認規則
咱們能夠經過zuul.ignored-services=order-service 配置須要忽略的微服務(多個微服務經過逗號隔開),這樣就不會自動對其建立路由規則。
咱們也可使用zuul.ignored-services=* 對全部的服務都不自動建立路由規則。
路徑匹配
不管是使用傳統路由的配置方式仍是服務路由的配置方式,咱們都須要爲每一個路由規則定義匹配表達式,也就是上面所說的path
參數。在Zuul中,路由匹配的路徑表達式採用了Ant風格定義。
Ant風格的路徑表達式使用起來很是簡單,它一共有下面這三種通配符:
通配符 | 說明 |
---|---|
? | 匹配任意的單個字符 |
* | 匹配任意數量的字符 |
** | 匹配任意數量的字符,支持多級目錄 |
通配符實例演示:
/user-service/?
它能夠匹配/user-service/以後拼接一個任務字符的路徑,好比:/user-service/a、/user-service/b、/user-service/c
/user-service/*
它能夠匹配/user-service/以後拼接任意字符的路徑,好比:/user-service/a、/user-service/aaa、/user-service/bbb。可是它沒法匹配/user-service/a/b
/user-service/**
它能夠匹配/user-service/*包含的內容以外,還能夠匹配形如/user-service/a/b的多級目錄路徑
忽略表達式
經過path參數定義的ant表達式已經可以完成api網關上的路由規則配置功能,可是爲了更細粒度和更爲靈活地配置理由規則,zuul還提供了一個忽略表達式參數zuul.ignored-patterns。
該參數能夠用來設置不但願被api網關進行路由的url表達式。
注意:該參數在使用時還須要注意它的範圍並非針對某個路由,而是對全部路由。因此在設置的時候須要全面考慮url規則,防止忽略了不應被忽略的url路徑。
好比咱們啓動order-service服務,訪問:http://192.168.1.57:6069/order/index
可使用api網關路由:http://192.168.1.57:6069/order-service/order/index
在zuul-service中配置
spring: application: name: zuul-serviceeureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} prefer-ip-address: trueserver: port: 6069zuul:ignoredPatterns: /**/index/** routes: user-service: path: /user-service/** serviceId: user-service order-service: path: /order-service/** serviceId: order-servicelogging: level: com.netflix: debug
設置後若是訪問 http://localhost:8888/order-service/index 將不會被正確路由,由於該路徑符合zuul.ignored-patterns 參數定義的規則。而其餘路徑則不會有問題,好比http://localhost:8888/order-service/getOrderInfo。
路由前綴
Zuul經過zuul.prefix
參數來爲路由規則增長前綴信息。
zuul: routes: user-service: path: /user-service/** serviceId: user-service order-service: path: /order-service/** serviceId: order-service prefix: /javaer123456
配置完前綴以後,以前訪問路徑都要增長 /javer123456前綴:
未加前綴時訪問 user-service 服務:http://localhost:8888/user-service/hello
添加前綴後訪問 hello-service 服務:http://localhost:8888/javaer123456/user-service/hello
Zuul經過strip-prefix代理前綴默認會從請求路徑中移除,經過該設置關閉移除功能
當 stripPrefix=true 的時 (會移除)
(http://127.0.0.1:8888/javaer123456/user/list-> http://192.168.1.100:8080/user/list)
當stripPrefix=false的時(不會移除)
http://127.0.0.1:8888/javaer123456/user/list ->http://192.168.1.100:8080/javaer123456/user/list
本地跳轉
在 Zuul 實現的 API 網關路由功能中,還支持 forward 形式的服務端跳轉配置。
好比咱們在 API 網關項目中增長一個 /local/helloWorld 的接口
@RestControllerpublic class HelloController { @RequestMapping("/local/helloWorld") public String hello() { return "welcome to Java學習部落"; }}
而後在 增長一個本地跳轉的路由規則(forward-local):
zuul.routes.forward-local.path=/forward-local/**zuul.routes.forward-local.url=forward:/local
當 API 網關接收到請求 /forward-local/helloWorld,它符合 forward-local 的路由規則,因此該請求會被 API 網關轉發到網關的 /local/helloWorld請求上進行本地處理。
cookie與頭信息
Spring Cloud Zuul 在請求路由時,經過zuul.sensitiveHeaders 參數定義,包括Cookie、Set-Cookie、Authorization 三個屬性來過濾掉HTTP請求頭信息中的一些敏感信息,防止它們被傳遞到下游的外部服務器。
可是若是咱們將使用了Spring Security、Shiro 等安全框架構建的 Web 應用經過 Spring Cloud Zuul 構建的網關來進行路由時,Cookie 信息沒法傳遞,會致使沒法實現 登陸和鑑權。
如何解決:
經過指定路由的參數來設置,僅對指定的web應用開啓敏感信息傳遞
# 對指定路由開啓自定義敏感頭zuul.routes.<router>.customSensitiveHeaders=true# 將指定路由的敏感頭信息設置爲空zuul.routes.<router>.sensitiveHeaders=[這裏設置要過濾的敏感頭]
注意:指定路由的敏感頭配置會覆蓋掉全局設置
重定向問題
什麼是Zuul的重定向問題?
咱們在瀏覽器中經過 Zuul 網關發起了認證服務,認證經過後會進行重定向到某個主頁或歡迎頁面。此時,咱們發現,在認證完成以後,可是發現重定向的這個歡迎頁的 host 變成了這個認證服務的 host,而不是 Zuul 的 host,這是一個很嚴重的問題。
解決方法:
### 網關配置zuul: routes: demo-order: path: /do/** serviceId: demo-order stripPrefix: true sensitiveHeaders: Cookie,Set-Cookie,Authorization # 此處解決後端服務重定向致使用戶瀏覽的 host 變成 後端服務的 host 問題 add-host-header: true
Zuul核心知識點---過濾器
過濾器能夠說是zuul實現api網關功能最核心的部件,Zuul大部分功能都是經過過濾器來實現的。每個進入zuul的http請求都會通過一系列的過濾器處理鏈獲得請求響應並返回給客戶端。
咱們能夠在ZuulFilter接口中看到定義的4個抽象方法
String filterType();int filterOrder();boolean shouldFilter();Object run();
這是個抽象方法也就表明了過濾器的四個核心概念
(filterType)類型Type:定義在路由流程中,過濾器被應用的階段
(filterOrder)執行順序Execution Order:在同一個Type中,定義過濾器執行的順序
(shouldFilter)條件Criteria:過濾器被執行必須知足的條件
(run)動做Action:過濾器的具體邏輯。在該函數中,咱們能夠實現自定義的過濾邏輯,來肯定是否要攔截當前的請求,不對其進行後續的路由,或是在請求路由返回結果以後,對處理結果作一些加工等。
類型Type
Zuul中定義了四種標準過濾器類型,這些過濾器類型對應於請求的典型生命週期。
(1) PRE:這種過濾器在請求被路由以前調用。咱們可利用這種過濾器實現身份驗證、在集羣中選擇請求的微服務、記錄調試信息等。
(2) ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。
(3) POST:這種過濾器在路由到微服務之後執行。這種過濾器可用來爲響應添加標準的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
核心過濾器詳解
核心過濾器執行順序
前置過濾器
1.ServletDetectionFilter
它的執行順序爲-3,是最早被執行的過濾器。
該過濾器老是會被執行,主要用來檢測當前請求是經過Spring的DispatcherServlet處理運行的,仍是經過ZuulServlet來處理運行的。
它的檢測結果會以布爾類型保存在當前請求上下文的isDispatcherServletRequest參數中,這樣後續的過濾器中,咱們就能夠經過RequestUtils.isDispatcherServletRequest()和RequestUtils.isZuulServletRequest()方法來判斷請求處理的源頭,以實現後續不一樣的處理機制。
通常狀況下,發送到api網關的外部請求都會被Spring的DispatcherServlet處理,除了經過/zuul/*路徑訪問的請求會繞過DispatcherServlet(好比以前咱們說的大文件上傳),被ZuulServlet處理,主要用來應對大文件上傳的狀況。
另外,對於ZuulServlet的訪問路徑/zuul/*,咱們能夠經過zuul.servletPath參數進行修改。
2.Servlet30WrapperFilter
它的執行順序爲-2,是第二個執行的過濾器,目前的實現會對全部請求生效,主要爲了將原始的HttpServletRequest
包裝成Servlet30RequestWrapper
對象。
3.FormBodyWrapperFilter
它的執行順序爲-1,是第三個執行的過濾器。該過濾器僅對兩類請求生效,第一類是Context-Type爲application/x-www-form-urlencoded的請求,第二類是Context-Type爲multipart/form-data而且是由String的DispatcherServlet處理的請求(用到了ServletDetectionFilter的處理結果)。
而該過濾器的主要目的是將符合要求的請求體包裝成FormBodyRequestWrapper對象。
4.DebugFilter
它的執行順序爲1,是第四個執行的過濾器,該過濾器會根據配置參數zuul.debug.request
和請求中的debug參數來決定是否執行過濾器中的操做。
而它的具體操做內容是將當前請求上下文中的debugRouting
和debugRequest
參數設置爲true。
因爲在同一個請求的不一樣生命週期均可以訪問到這二個值,因此咱們在後續的各個過濾器中能夠利用這二個值來定義一些debug信息,這樣當線上環境出現問題的時候,能夠經過參數的方式來激活這些debug信息以幫助分析問題,另外,對於請求參數中的debug參數,咱們能夠經過zuul.debug.parameter
來進行自定義。
5.PreDecorationFilter
執行順序是5,是pre階段最後被執行的過濾器,該過濾器會判斷當前請求上下文中是否存在forward.do
和serviceId
參數,若是都不存在,那麼它就會執行具體過濾器的操做(若是有一個存在的話,說明當前請求已經被處理過了,由於這二個信息就是根據當前請求的路由信息加載進來的)。
而當它的具體操做內容就是爲當前請求作一些預處理,好比說,進行路由規則的匹配,在請求上下文中設置該請求的基本信息以及將路由匹配結果等一些設置信息等,這些信息將是後續過濾器進行處理的重要依據,咱們能夠經過RequestContext.getCurrentContext()
來訪問這些信息。
另外,咱們還能夠在該實現中找到對HTTP頭請求進行處理的邏輯,其中包含了一些耳熟能詳的頭域,好比X-Forwarded-Host
,X-Forwarded-Port
。
另外,對於這些頭域是經過zuul.addProxyHeaders
參數進行控制的,而這個參數默認值是true,因此zuul在請求跳轉時默認會爲請求增長X-Forwarded-*
頭域,包括X-Forwarded-Host
,X-Forwarded-Port
,X-Forwarded-For
,X-Forwarded-Prefix
,X-Forwarded-Proto
。
也能夠經過設置zuul.addProxyHeaders=false
關閉對這些頭域的添加動做。
路由過濾器
6.RibbonRoutingFilter
它的執行順序爲10,是route階段的第一個執行的過濾器。
該過濾器只對請求上下文中存在serviceId參數的請求進行處理,即只對經過serviceId配置路由規則的請求生效。
而該過濾器的執行邏輯就是面向服務路由的核心,它經過使用ribbon和hystrix來向服務實例發起請求,並將服務實例的請求結果返回。
7.SimpleHostRoutingFilter
它的執行順序爲100,是route階段的第二個執行的過濾器。
該過濾器只對請求上下文存在routeHost參數的請求進行處理,即只對經過url配置路由規則的請求生效。
而該過濾器的執行邏輯就是直接向routeHost參數的物理地址發起請求,從源碼中咱們能夠知道該請求是直接經過httpclient包實現的,而沒有使用Hystrix命令進行包裝,因此這類請求並無線程隔離和斷路器的保護。
知道配置相似zuul.routes.user-service.url=http://localhost:8080/這樣的底層都是經過httpclient直接發送請求的,也就知道爲何這樣的狀況沒有作到負載均衡的緣由所在。
8.SendForwardFilter
它的執行順序是500,是route階段第三個執行的過濾器。該過濾器只對請求上下文中存在的forward.do參數進行處理請求,即用來處理路由規則中的forward本地跳轉裝配。
後置過濾器
9.SendErrorFilter
它的執行順序是0,是post階段的第一個執行的過濾器。該過濾器僅在請求上下文中包含error.status_code參數(由以前執行的過濾器設置的錯誤編碼)而且尚未被該過濾器處理過的時候執行。而該過濾器的具體邏輯就是利用上下文中的錯誤信息來組成一個forward到api網關/error錯誤端點的請求來產生錯誤響應。
10.SendResponseFilter
它的執行順序爲1000,是post階段最後執行的過濾器,該過濾器會檢查請求上下文中是否包含請求響應相關的頭信息,響應數據流或是響應體,只有在包含它們其中一個的時候執行處理邏輯。
而該過濾器的處理邏輯就是利用上下文的響應信息來組織須要發送回客戶端的響應內容。
如何禁用過濾器
Spring Cloud默認爲Zuul編寫並啓用了一些過濾器,一些場景下,想要禁用掉部分過濾器,此時該怎麼辦呢?
只需設置zuul.<SimpleClassName>.<filterType>.disable=true
,便可禁用SimpleClassName
所對應的過濾器。
以過濾器org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter
爲例,只需設置zuul.SendResponseFilter.post.disable=true
,便可禁用該過濾器。
Zuul自定義過濾器
由上文可知,咱們能夠將Zuul的過濾器分爲pre,route,post三大類型。本文咱們將自定義一個pre類型的過濾器。
首先要自定義一個過濾器,只須要完成如下幾個步驟。
1.繼承ZuulFilter
2.指定過濾類型、過濾順序
3.是否執行這個過濾器、過濾內容
1.首先定一個抽象類 AbstractZuulFilter.java 繼承ZuulFilter
package com.javaer.study.zuul.core.zuul.filter;import com.netflix.zuul.ZuulFilter;import com.netflix.zuul.context.RequestContext;import com.javaer.study.zuul.core.zuul.ContantValue;/** * @Author 公衆號【Java學習部落】 * * 關注公衆號獲取4000G精品視頻資源 * * 更有更多學習Java後端、中間件,大數據、微服務、分佈式、大數據等學習乾貨 **/public abstract class AbstractZuulFilter extends ZuulFilter { protected RequestContext context; @Override public boolean shouldFilter() { RequestContext ctx = RequestContext.getCurrentContext(); return (boolean) (ctx.getOrDefault(ContantValue.NEXT_FILTER, true)); } @Override public Object run() { context = RequestContext.getCurrentContext(); return doRun(); } public abstract Object doRun(); public Object fail(Integer code, String message) { context.set(ContantValue.NEXT_FILTER, false); context.setSendZuulResponse(false); context.getResponse().setContentType("text/html;charset=UTF-8"); context.setResponseStatusCode(code); context.setResponseBody(String.format("{\"result\":\"%s!\"}", message)); return null; } public Object success() { context.set(ContantValue.NEXT_FILTER, true); return null; }}
2.定義preFilter的抽象類,繼承AbstractZuulFilter。指定過濾器類型爲pre類型。
package com.javaer.study.zuul.core.zuul.filter.pre;import com.javaer.study.zuul.core.zuul.FilterType;import com.javaer.study.zuul.core.zuul.filter.AbstractZuulFilter;/** * @Author 公衆號【Java學習部落】 * * 關注公衆號獲取4000G精品視頻資源 * * 更有更多學習Java後端、中間件,大數據、微服務、分佈式、大數據等學習乾貨 **/public abstract class AbstractPreZuulFilter extends AbstractZuulFilter { @Override public String filterType() { return FilterType.pre.name(); }}
3.接着編寫具體一個具體的限流過濾器
package com.javaer.study.zuul.core.zuul.filter.pre;import com.google.common.util.concurrent.RateLimiter;import com.syher.zuul.core.zuul.FilterOrder;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletRequest;/** * @Author 公衆號【Java學習部落】 * * 關注公衆號獲取4000G精品視頻資源 * * 更有更多學習Java後端、中間件,大數據、微服務、分佈式、大數據等學習乾貨 **/public class RateLimiterFilter extends AbstractPreZuulFilter { private static final Logger LOGGER = LoggerFactory.getLogger(RateLimiterFilter.class); /** * 每秒容許處理的量是50 */ RateLimiter rateLimiter = RateLimiter.create(50); @Override public int filterOrder() { return FilterOrder.RATE_LIMITER_ORDER; } @Override public Object doRun() { HttpServletRequest request = context.getRequest(); String url = request.getRequestURI(); if (rateLimiter.tryAcquire()) { return success(); } else { LOGGER.info("rate limit:{}", url); return fail(401, String.format("rate limit:{}", url)); } }}
4.最後建立一個配置類,將下列過濾器託管給spring。
package com.javaer.study.zuul.core.config;import com.netflix.zuul.ZuulFilter;import com.syher.zuul.core.zuul.filter.pre.RateLimiterFilter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * @Author 公衆號【Java學習部落】 * * 關注公衆號獲取4000G精品視頻資源 * * 更有更多學習Java後端、中間件,大數據、微服務、分佈式、大數據等學習乾貨 **/@Configurationpublic class ZuulConfigure { /** * 自定義過濾器 * @return */ @Bean public ZuulFilter rateLimiterFilter() { return new RateLimiterFilter(); }}
Zuul 容錯與回退
看過以前講解什麼是Hystrix,阿里技術最終面,遺憾的倒在Hystrix面前的文章的同窗們應該都知道,不少時候,不管是由於服務節點的重啓,宕機仍是因爲網絡故障,咱們在訪問某一個服務節點時可能會出現阻塞或者異常。
Zuul進行路由時候也會由於這些緣由出現異常。
此時若是咱們直接將異常信息展現給用戶的話確定是很不友好的,咱們須要展現給用戶的是用戶能看的明白的形成訪問失敗的緣由。
這裏咱們就能夠用到Zuul的回退處理了。
SpringCloud中使用Hystrix實現微服務的容錯與回退,其實Zuul默認已經整合了Hystrix。
實現Zuul添加回退
要實現Zuul添加回退,須要實現ZuulFallbackProvider接口,而後在實現類中,指定爲哪一個微服務提供回退,並提供一個ClientHttpResponse做爲回退響應。
package com.javaer.study.zuul;import com.alibaba.fastjson.JSONObject;import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.client.ClientHttpResponse;import org.springframework.stereotype.Component;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;/** * @Author 公衆號【Java學習部落】 * * 關注公衆號獲取4000G精品視頻資源 * * 更有更多學習Java後端、中間件,大數據、微服務、分佈式、大數據等學習乾貨 **/@Componentpublic class ZuulFallBack implements FallbackProvider { /**爲哪一個服務提供回退,*號表明全部服務**/ @Override public String getRoute() { return "order-service"; //根據服務id指定爲哪一個微服務提供回退,能夠用* 或者 null 表明全部服務// } /**回退響應**/ @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { return new ClientHttpResponse() { /**回退時的狀態碼**/ @Override public HttpStatus getStatusCode() throws IOException { //請求網關成功了,因此是ok return HttpStatus.OK; } /**數字類型狀態碼**/ @Override public int getRawStatusCode() throws IOException { return HttpStatus.OK.value(); } /**狀態文本**/ @Override public String getStatusText() throws IOException { return HttpStatus.OK.getReasonPhrase(); } /****/ @Override public void close() { } /**響應體**/ @Override public InputStream getBody() throws IOException { JSONObject json =new JSONObject(); json.put("state","501"); json.put("msg","後臺接口錯誤"); //返回前端的內容 return new ByteArrayInputStream(json.toJSONString().getBytes("UTF-8")); } /**返回的響應頭**/ @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders = new HttpHeaders(); //設置頭 return httpHeaders; httpHeaders.setContentType(MediaType.APPLICATION_JSON); } }; }}
注意:在Spring cloud Edgware版本以前,要想回退,需實現ZuulFallBackProvider接口,從Spring cloud Edgware版本以後,實現FallbackProvider接口。
總結
本文從什麼是網關和爲何要使用網關引入SpringCloud Zuul,介紹了什麼是Zuul,Zuul的使用場景。而且分析了Zuul的核心架構以及Zuul請求處理的生命週期。而後着重的講解了Zuul的路由和過濾器這兩個在任何網關中都十分重要的組件。本文旨在帶領你們對Zuul有一個全局的認識。在往後學習其它網關技術的時候也可以融會貫通,學習起來可以事半功倍。