Spring Cloud Gateway 之Predict篇

Spring Cloud gateway工作流程

在之前的文章的Spring Cloud Gateway初體驗中,大家已經對Spring Cloud Gateway的功能有一個初步的認識,網關作為一個系統的流量的入口,有著舉足輕重的作用,通常的作用如下:

  • 協議轉換,路由轉發
  • 流量聚合,對流量進行監控,日誌輸出
  • 作為整個系統的前端工程,對流量進行控制,有限流的作用
  • 作為系統的前端邊界,外部流量只能通過網關才能訪問系統
  • 可以在網關層做權限的判斷
  • 可以在網關層做緩存

Spring Cloud Gateway作為Spring Cloud框架的第二代網關,在功能上要比Zuul更加的強大,性能也更好。隨著Spring Cloud的版本迭代,Spring Cloud官方有打算棄用Zuul的意思。在筆者調用了Spring Cloud Gateway的使用和功能上,Spring Cloud Gateway替換掉Zuul的成本上是非常低的,幾乎可以無縫切換。Spring Cloud Gateway幾乎包含了zuul的所有功能。

Spring Cloud Gateway 之Predict篇

注:該圖片來自官網

如上圖所示,客戶端向Spring Cloud Gateway發出請求。 如果Gateway Handler Mapping確定請求與路由匹配(這個時候就用到predicate),則將其發送到Gateway web handler處理。 Gateway web handler處理請求時會經過一系列的過濾器鏈。 過濾器鏈被虛線劃分的原因是過濾器鏈可以在發送代理請求之前或之後執行過濾邏輯。 先執行所有“pre”過濾器邏輯,然後進行代理請求。 在發出代理請求之後,收到代理服務的響應之後執行“post”過濾器邏輯。這跟zuul的處理過程很類似。在執行所有“pre”過濾器邏輯時,往往進行了鑑權、限流、日誌輸出等功能,以及請求頭的更改、協議的轉換;轉發之後收到響應之後,會執行所有“post”過濾器的邏輯,在這裡可以響應數據進行了修改,比如響應頭、協議的轉換等。

在上面的處理過程中,有一個重要的點就是講請求和路由進行匹配,這時候就需要用到predicate,它是決定了一個請求走哪一個路由。

predicate簡介

Predicate來自於java8的接口。Predicate 接受一個輸入參數,返回一個布爾值結果。該接口包含多種默認方法來將Predicate組合成其他複雜的邏輯(比如:與,或,非)。可以用於接口請求參數校驗、判斷新老數據是否有變化需要進行更新操作。add--與、or--或、negate--非。

Spring Cloud Gateway內置了許多Predict,這些Predict的源碼在org.springframework.cloud.gateway.handler.predicate包中,如果讀者有興趣可以閱讀一下。現在列舉各種Predicate如下圖:

Spring Cloud Gateway 之Predict篇

在上圖中,有很多類型的Predicate,比如說時間類型的Predicated(AfterRoutePredicateFactory BeforeRoutePredicateFactory BetweenRoutePredicateFactory),當只有滿足特定時間要求的請求會進入到此predicate中,並交由router處理;cookie類型的CookieRoutePredicateFactory,指定的cookie滿足正則匹配,才會進入此router;以及host、method、path、querparam、remoteaddr類型的predicate,每一種predicate都會對當前的客戶端請求進行判斷,是否滿足當前的要求,如果滿足則交給當前請求處理。如果有很多個Predicate,並且一個請求滿足多個Predicate,則按照配置的順序第一個生效。

predicate實戰

現在以案例的形式來講解predicate,本文中的案例基本來源於官方文檔,官方文檔地址:http://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.0.0.RELEASE/single/spring-cloud-gateway.html ;如果有任何問題歡迎和我聯繫,和我討論。

創建一個工程,在工程的pom文件引入spring cloud gateway 的起步依賴spring-cloud-starter-gateway,spring cloud版本和spring boot版本,代碼如下:

 
<parent>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-parent/<artifactid>
<version>2.0.5.RELEASE/<version>
/<parent>
<dependencymanagement>
<dependencies>
<dependency>
<groupid>org.springframework.cloud/<groupid>
<artifactid>spring-cloud-dependencies/<artifactid>
<version>Finchley.SR1/<version>
<type>pom/<type>
<scope>import/<scope>
/<dependency>
/<dependencies>

/<dependencymanagement>
<dependency>
<groupid>org.springframework.cloud/<groupid>
<artifactid>spring-cloud-starter-gateway/<artifactid>
/<dependency>

After Route Predicate Factory

AfterRoutePredicateFactory,可配置一個時間,當請求的時間在配置時間之後,才交給 router去處理。否則則報錯,不通過路由。

在工程的application.yml配置如下:

server:
port: 8081
spring:
profiles:
active: after_route
---
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://httpbin.org:80/get
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
profiles: after_route

在上面的配置文件中,配置了服務的端口為8081,配置spring.profiles.active:after_route指定了程序的spring的啟動文件為after_route文件。在application.yml再建一個配置文件,語法是三個橫線,在此配置文件中通過spring.profiles來配置文件名,和spring.profiles.active一致,然後配置spring cloud gateway 相關的配置,id標籤配置的是router的id,每個router都需要一個唯一的id,uri配置的是將請求路由到哪裡,本案例全部路由到http://httpbin.org:80/get。

predicates: After=2017-01-20T17:42:47.789-07:00[America/Denver] 會被解析成PredicateDefinition對象 (name =After ,args= 2017-01-20T17:42:47.789-07:00[America/Denver])。在這裡需要注意的是predicates的After這個配置,遵循的契約大於配置的思想,它實際被AfterRoutePredicateFactory這個類所處理,這個After就是指定了它的Gateway web handler類為AfterRoutePredicateFactory,同理,其他類型的predicate也遵循這個規則。

當請求的時間在這個配置的時間之後,請求會被路由到http://httpbin.org:80/get。

啟動工程,在瀏覽器上訪問http://localhost:8081/,會顯示http://httpbin.org:80/get返回的結果,此時gateway路由到了配置的uri。如果我們將配置的時間設置到當前時之後,瀏覽器會顯示404,此時證明沒有路由到配置的uri.

跟時間相關的predicates還有Before Route Predicate Factory、Between Route Predicate Factory,讀者可以自行查閱官方文檔,再次不再演示。

Header Route Predicate Factory

Header Route Predicate Factory需要2個參數,一個是header名,另外一個header值,該值可以是一個正則表達式。當此斷言匹配了請求的header名和值時,斷言通過,進入到router的規則中去。

在工程的配置文件加上以下的配置:

spring:

profiles:

active: header_route

---

spring:

cloud:

gateway:

routes:

- id: header_route

uri: http://httpbin.org:80/get

predicates:

- Header=X-Request-Id, \d+

profiles: header_route

在上面的配置中,當請求的Header中有X-Request-Id的header名,且header值為數字時,請求會被路由到配置的 uri. 使用curl執行以下命令:

$ curl -H 'X-Request-Id:1' localhost:8081

執行命令後,會正確的返回請求結果,結果省略。如果在請求中沒有帶上X-Request-Id的header名,並且值不為數字時,請求就會報404,路由沒有被正確轉發。

Cookie Route Predicate Factory

Cookie Route Predicate Factory需要2個參數,一個時cookie名字,另一個時值,可以為正則表達式。它用於匹配請求中,帶有該名稱的cookie和cookie匹配正則表達式的請求。

在配置文件添加以下配置:


spring:
profiles:
active: cookie_route

---
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://httpbin.org:80/get
predicates:
- Cookie=name, forezp
profiles: cookie_route

在上面的配置中,請求帶有cookie名為 name, cookie值為forezp 的請求將都會轉發到uri為 http://httpbin.org:80/get的地址上。 使用curl命令進行請求,在請求中帶上 cookie,會返回正確的結果,否則,請求報404錯誤。

$ curl -H 'Cookie:name=forezp' localhost:8081

Host Route Predicate Factory

Host Route Predicate Factory需要一個參數即hostname,它可以使用. * 等去匹配host。這個參數會匹配請求頭中的host的值,一致,則請求正確轉發。

在工程的配置文件,加上以下配置:


spring:
profiles:
active: host_route
---
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://httpbin.org:80/get
predicates:
- Host=**.fangzhipeng.com
profiles: host_route

在上面的配置中,請求頭中含有Host為fangzhipeng.com的請求將會被路由轉發轉發到配置的uri。 啟動工程,執行以下的curl命令,請求會返回正確的請求結果:

curl -H 'Host:www.fangzhipeng.com' localhost:8081

Method Route Predicate Factory

Method Route Predicate Factory 需要一個參數,即請求的類型。比如GET類型的請求都轉發到此路由。在工程的配置文件加上以下的配置:

spring:

profiles:

active: method_route

---

spring:

cloud:

gateway:

routes:

- id: method_route

uri: http://httpbin.org:80/get

predicates:

- Method=GET

profiles: method_route

在上面的配置中,所有的GET類型的請求都會路由轉發到配置的uri。使用 curl命令模擬 get類型的請求,會得到正確的返回結果。

$ curl localhost:8081

使用 curl命令模擬 post請求,則返回404結果。

$ curl -XPOST localhost:8081

Path Route Predicate Factory

Path Route Predicate Factory 需要一個參數: 一個spel表達式,應用匹配路徑。

在工程的配置文件application.yml文件中,做以下的配置:

spring:

profiles:

active: path_route

---

spring:

cloud:

gateway:

routes:

- id: path_route

uri: http://httpbin.org:80/get

predicates:

- Path=/foo/{segment}

profiles: path_route

在上面的配置中,所有的請求路徑滿足/foo/{segment}的請求將會匹配並被路由,比如/foo/1 、/foo/bar的請求,將會命中匹配,併成功轉發。

使用curl模擬一個請求localhost:8081/foo/dew,執行之後會返回正確的請求結果。

$ curl localhost:8081/foo/dew

Query Route Predicate Factory

Query Route Predicate Factory 需要2個參數:一個參數名和一個參數值的正則表達式。在工程的配置文件application.yml做以下的配置:


spring:
profiles:
active: query_route
---
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://httpbin.org:80/get
predicates:
- Query=foo, ba.
profiles: query_route

在上面的配置文件中,配置了請求中含有參數foo,並且foo的值匹配ba.,則請求命中路由,比如一個請求中含有參數名為foo,值的為bar,能夠被正確路由轉發。

模擬請求的命令如下:

$ curl localhost:8081?foo=bar

Query Route Predicate Factory也可以只填一個參數,填一個參數時,則只匹配參數名,即請求的參數中含有配置的參數名,則命中路由。比如以下的配置中,配置了請求參數中含有參數名為foo 的參數將會被請求轉發到uri為http://httpbin.org:80/get。


spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://httpbin.org:80/get
predicates:
- Query=foo
profiles: query_route

總結

在本篇文章中,首先介紹了Spring Cloud Gateway的工作流程和原理,然後介紹了gateway框架內置的predict及其分類,最後以案例的形式重點講解了幾個重要的Predict。Predict作為斷言,它決定了請求會被路由到哪個router 中。在斷言之後,請求會被進入到filter過濾器的邏輯,下篇文章將會為大家介紹Spring Cloud Gateway過濾器相關的內容。


分享到:


相關文章: