詳解SpringCloud Gateway 2020.0.2最新版
簡述
官網:https://spring.io/projects/spring-cloud-gateway
GitHub地址:https://github.com/spring-cloud/spring-cloud-gateway
本文編寫自2021年4月7日,當前SpringCloud最新版本為2020.0.2版本
本文使用版本為
SpringCloud 版本2020.0.2
spring-cloud-starter-gateway版本3.0.2
spring-boot-starter版本2.4.4
該項目提供瞭一個用於在Spring WebFlux之上構建API網關的庫。 Spring Cloud Gateway旨在提供一種簡單而有效的方法來路由到API,並為它們提供跨領域的關註點,例如:安全性,監視/指標和彈性。
特征
- 建立在Spring Framework 5,Project Reactor和Spring Boot 2.0之上
- 能夠匹配任何請求屬性上的路由。
- 謂詞和過濾器特定於路由。
- 斷路器集成。
- Spring Cloud DiscoveryClient集成
- 易於編寫的謂詞和過濾器
- 請求速率限制
- 路徑改寫
概念
什麼是路由?
路由是構建網關的基本模塊,由ID,目標URI,一系列的斷言和過濾器組成,如果斷言為true,則匹配該路由
什麼是斷言?
開發人員可以匹配HTTP請求中的所有內容(例如請求頭或請求參數),如果請求與斷言相匹配則進行路由
什麼是過濾?
值得是Spring框架中GatewayFilter的實例,使用過濾器,可以使請求在被路由前/後進行修改
然後讓我們先通過幾個小demo先瞭解一下gateway的大概使用,然後我們在深入瞭解更多相關知識,這樣比較容易理解。
獨立版代碼(靜態路由)
依賴坐標
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.4.4</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>3.0.2</version> </dependency>
配置
配置文件、配置類選擇一個就ok
根據此配置,訪問 http:localhost:9527/s 將直接轉發到百度首頁,頁面內容展示百度首頁內容
application.yml
server: port: 9527 spring: application: name: cloud-gateway # 以下是gateway的配置 cloud: gateway: routes: - id: gateway_route_1 uri: https://www.baidu.com/s predicates: - Path=/s/**
GatewayConfig.java org.example.springcloud.config.GatewayConfig
package org.example.springcloud.config; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Auth: guohui.wang * @Date: 2021-04-07 11:07 * @desc: */ @Configuration public class GatewayConfig { /** * <p>將 "ip:${server.port}+${route.path}" 轉發到 "${route.url}"</p> * <p>例 "120.0.0.1:9527/s" 轉發到 "https://www.baidu.com/s"</p><br> * <p>可以嘗試訪問以下鏈接獲取配置效果</p> * <li>"http://localhost:9527/s"</li> * <li>"http://localhost:9527/s?wd=spring"</li> * <br> * <div> * <b>routes().route(參數1,參數2).route(參數1,參數2)……</b> * <li>參數1:id,一個可以隨便取得名字,標識此路由規則</li> * <li>參數2:具體的路由規則內容</li> * * <b>路由規則:</b> * <li>path: 網關服務地址之後的內容(比如上面的"/s")</li> * <li>uri: 符合path之後需要轉發到的位置</li> * </div> * * @param builder * @return */ @Bean public RouteLocator routes(RouteLocatorBuilder builder) { return builder .routes() .route("gateway_route_1", r -> r.path("/s") .uri("https://www.baidu.com/s") ) .build(); } }
啟動類
org.example.springcloud.GatewayMain9527
package org.example.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class GatewayMain9527 { public static void main(String[] args) { SpringApplication.run(GatewayMain9527.class, args); } }
與erueka整合版(動態路由)
通過微服務名實現動態路由,這樣可以在服務中心註冊的對應的多個服務之間來回調用瞭。
需要三個微服務,服務結構:
父項目
- eureka服務,作為註冊中心
- gateway服務,作為網關
- provider服務,作為網關轉發的服務
父項目
創建項目,選擇maven(個人喜好也可選擇SpringInitializr)->輸入名稱及相關信息(我這裡起的名字是 springcloudgateway)->點擊finish創建項目
修改pom,pom中添加版本控制
<dependencyManagement> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.4.4</version> <type>pom</type> <scope>import</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2020.0.2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
eureka模塊
創建項目,選擇maven(個人喜好也可選擇SpringInitializr)->輸入相關信息->Finish
我這裡用的模塊名:cloud-eureka
pom中添加坐標依賴
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies>
創建類EurekaApplication org.example.springcloud.eureka.EurekaApplication
package org.example.springcloud.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableEurekaServer public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
在main/resources下面創建application.yml 並添加配置
server: port: 9001 eureka: instance: hostname: localhost client: # 不向註冊中心註冊自己 register-with-eureka: false # 表示自己端就是註冊中心,不需要檢索服務 fetch-registry: false service-url: # 設置與eureka server交互地址查詢服務和註冊服務都需要依賴這個地址 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
驗證
啟動服務沒有問題(控制臺內容略),並且訪問 http://localhost:9001/ 可以打開頁面:
provider模塊
這個模塊假裝作為提供服務的微服務,用來測試網關是否生效的。
創建項目,選擇maven(個人喜好也可選擇SpringInitializr)->輸入相關信息->Finish
我這裡用的模塊名:cloud-provider
pom中添加坐標依賴
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies>
創建類ProviderApplication org.example.springcloud.provider.ProviderApplication
package org.example.springcloud.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } }
創建controller層類TestController org.example.springcloud.provider.controller.TestController
package org.example.springcloud.provider.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/test") @RestController public class TestController { @GetMapping("/lb") public String lb() { return "訪問成功"; } }
在main/resources下面創建application.yml 並添加配置
server: port: 9003 spring: application: name: cloud-provider #eureka相關配置 eureka: instance: instance-id: cloud-provider-01 client: service-url: defaultZone: http://127.0.0.1:9001/eureka register-with-eureka: true fetch-registry: true
驗證
服務啟動沒問題,並且訪問 http://localhost:9003/test/lb 可以獲取到返回內容,說明接口沒問題。
並且打開eureka頁面 http://localhost:9001/ 可以看到服務已經註冊進去,說明服務註冊也沒問題。
gateway模塊 創建項目,選擇maven(個人喜好也可選擇SpringInitializr)->輸入相關信息->Finish
我這裡用的模塊名:cloud-gateway
pom中添加坐標依賴
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies>
創建類GatewayApplication org.example.springcloud.gateway.GatewayApplication
package org.example.springcloud.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
在main/resources下面創建application.yml 並添加配置
server: port: 9002 spring: application: name: cloud-gateway cloud: gateway: discovery: locator: #開啟從註冊中心動態創建路由功能,利用微服務名進行路由 enabled: true routes: - id: payment_routh2 # lb:// + ${目標服務的spring.application.name的值} uri: lb://cloud-provider # 映射的路徑 訪問符合Path值的路徑,轉發到響應的uri對應服務下Path predicates: - Path=/test/lb/** #eureka相關配置 eureka: instance: instance-id: cloud-gateway-01 client: service-url: defaultZone: http://127.0.0.1:9001/eureka register-with-eureka: true fetch-registry: true
驗證
並且打開eureka頁面 http://localhost:9001/ 可以看到服務已經註冊進去,說明服務註冊沒問題。
根據配置訪問 http://localhost:9002/test/lb 如果出現正常返回頁面,則說明網關配置成功。
但此時應該是訪問失敗的,會出現如下頁面,並伴隨控制臺的如下報錯:
看,報錯內容找我們的主機名瞭,java.net.UnknownHostException: failed to resolve 'WRGHO-VIEZQZWFI' after 4 queries
中的 ‘WRGHO-VIEZQZWFI’ 就是我的主機名
不要著急,這時候我們往目標服務(也就是provider服務)中添加這樣一條配置就ok瞭:
此配置項表示:表示在猜測主機名時,應優先使用服務器的IP地址,而不要使用OS報告的主機名
然後重啟provider服務,重新訪問,如果訪問的時候報錯503,看gateway控制臺內容提示
No servers available for service: cloud-provider
,說明服務雖然重啟瞭但是還麼有註冊到服務中心裡,我們等一小會等待註冊進去再嘗試就ok瞭。
錯誤頁面和控制臺提示內容:
至此我們應該對gateway的使用方法有個大體的瞭解和印象瞭,然後讓我們繼續學習相關知識。
斷言(Predicate)
開發人員可以匹配HTTP請求中的所有內容(例如請求頭或請求參數),如果請求與斷言相匹配則進行路由。
也就是我們進行路由跳轉的條件。
https://docs.spring.io/spring-cloud-gateway/docs/3.0.2/reference/html/#gateway-request-predicates-factories
在之前我們的配置中使用瞭如下圖 Path 這樣一種斷言。
但是實際上我們還有好多種斷言,我們可以通過官方文檔查詢到,可以看我下面的連接,但是最好自己去官網找到自己響應版本的文檔看(鬼知道以後版本會不會出新的斷言),並且官方文檔有響應的例子以及解釋。
https://docs.spring.io/spring-cloud-gateway/docs/3.0.2/reference/html/#gateway-request-predicates-factories
在我們的gateway服務啟動時候,控制臺業務打印出來我們斷言的方式。
左圖官方文檔,右圖啟動控制臺信息。
Spring Cloud Gateway將路由匹配作為Spring WebFlux HandlerMapping基礎架構的一部分。 Spring Cloud Gateway包括許多內置的路由斷言工廠。所有這些謂詞都與HTTP請求的不同屬性匹配。您可以將多個路由斷言工廠與邏輯和語句結合使用。
斷言種類:
After
該斷言匹配在指定日期時間之後發生的請求。
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
Before
該斷言匹配在指定日期時間之前發生的請求。
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
Between 該斷言匹配在兩個指定日期時間之間發生的請求。
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
Cookie
該斷言采用兩個參數,即cookie名稱和一個regexp(這是Java正則表達式)。該斷言匹配具有給定名稱且其值與正則表達式匹配的cookie。
- Cookie=chocolate, ch.p
Header
該斷言采用兩個參數,header名稱和一個regexp(這是Java正則表達式)。該斷言與具有給定名稱的header匹配,該header的值與正則表達式匹配。
- Header=X-Request-Id, \d+
Host
該斷言采用一個參數:主機名模式列表。該模式是帶有的Ant樣式的模式。作為分隔符。
該斷言匹配與模式匹配的Host標頭。
- Host=**.somehost.org,**.anotherhost.org
Method
該斷言采用方法參數,該參數是一個或多個參數:要匹配的HTTP方法。
- Method=GET,POST
Path
該斷言采用兩個參數:Spring PathMatcher模式列表和一個稱為matchTrailingSlash的可選標志(默認為true)。
- Path=/red/{segment},/blue/{segment}
Query
該斷言采用兩個參數:必需的參數和可選的regexp(這是Java正則表達式)。如果請求包含匹配配置的查詢參數,則路由匹配。
- Query=green
RemoteAddr
該斷言采用sources列表(最小大小為1),這些源是CIDR標記(IPv4或IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是IP地址,而16是子網掩碼) )。
- RemoteAddr=192.168.1.1/24
ReadBody
文檔中沒寫這個,但是啟動的時候控制臺顯示瞭,RoutePredicateFactory的實現類ReadBodyRoutePredicateFactory寫瞭一點註釋。斷言可讀取主體並應用用戶提供的斷言在主體上運行。
主體被緩存在內存中,因此後續對斷言的調用無需再次反序列化。
Weight
文檔中沒寫這個,但是啟動的時候控制臺顯示瞭,RoutePredicateFactory的實現類WeightRoutePredicateFactory也沒寫啥註釋
過濾器(Filter)
GatewayFilter允許以某種方式修改傳入的HTTP請求或傳出的HTTP響應。路由過濾器的作用域是特定的路由。 Spring Cloud Gateway包括許多內置的GatewayFilter工廠。
GlobalFilter接口具有與GatewayFilter相同的簽名。這些是特殊過濾器,有條件地應用於所有路由。
https://docs.spring.io/spring-cloud-gateway/docs/3.0.2/reference/html/#gatewayfilter-factories
在我看的這個文檔中,有兩種Filter(GatewayFilter和GlobalFilter)
GatewayFilter官網給出瞭這麼31個過濾器工廠(工廠就是產生對象的,也就是相當於31個過濾器),
GlobalFilter官網給出瞭這麼10個過濾器工廠(工廠就是產生對象的,也就是相當於31個過濾器):
怎麼用呢?
就像是斷言(Predicate)一樣,在spring.cloud.gateway.routes
下面添加就好瞭,和id
、uri
同級。
例如:
自定義過濾器
我們自定義過濾器需要實現 GlobalFilter,Ordered
這兩個接口。
implements GlobalFilter,Ordered
能幹嘛?
- 全局日志記錄
- 統一網關鑒權
- ……
我們的自定義過濾器代碼
MyLogGatewayFilter.java org.example.springcloud.gateway.filter.MyLogGatewayFilter
package org.example.springcloud.gateway.filter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class MyLogGatewayFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 不用log.info瞭,為瞭省點事,別較真 System.out.println("進入到瞭我們的自定義日志過濾器"); // request、response啥的都能拿到,要幹啥自己看著辦吧,獲取在下兩行, // 你是要判斷參數啊還是判斷token啊還是咋地那你自己定義去吧。 // ServerHttpRequest request = exchange.getRequest(); // ServerHttpResponse response = exchange.getResponse(); // 返回chain.filter(exchange); 表示此過濾器通過,將請求繼續傳遞下去 // 交由下面的過濾器繼續處理 // 如果是 return null; 則表示被過濾掉瞭,沒通過。 return chain.filter(exchange); } @Override public int getOrder() { // 加載過濾器的順序,數值越小優先級越高。 // Integer.MIN_VALUE 到 Integer.MAX_VALUE return 0; } }
寫好瞭自定義過濾器就ok瞭,不需要進行什麼配置,直接@Component加入容器中就可以瞭。
比如我們在訪問之前的 http://localhost:9002/test/lb ,控制臺就會打印我們在過濾器中打印的內容,說明進入到瞭過濾器中。
過濾器很強大,怎麼用看個人、看需求,根據自己要實現的內容去定制化實現吧。
對應代碼文件
http://xiazai.jb51.net/202104/yuanma/springcloudgateway_jb51.rar
到此這篇關於SpringCloud Gateway 2020.0.2最新版的文章就介紹到這瞭,更多相關SpringCloud Gateway內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- None Found