一、SpringCloudGateway
1.1 簡介
SpringCloud Gateway 是 Spring Cloud 的一個全新項目,該項目是基於 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術開發的網關,它旨在為微服務架構提供一種簡單有效的統一的 API 路由管理方式。
SpringCloud Gateway 作為 Spring Cloud 生態系統中的網關,目標是替代 Zuul,在 Spring Cloud 2.0 以上版本中,沒有對新版本的 Zuul 2.0 以上最新高性能版本進行集成,仍然還是使用的 Zuul 2.0 之前的 非Reactor 模式 的老版本。而為了提升網關的性能,SpringCloud Gateway 是基於 WebFlux 框架實現的,而 WebFlux 框架底層則使用了高性能的 Reactor 模式通信框架 Netty。
Spring Cloud Gateway 不僅提供統一的路由方式,並且基於 Filter 鏈 的方式提供了網關基本的功能,例如:安全,監控/指標,和限流等。
1.2 名詞解釋
-
Filter(過濾器):
和 Zuul 的過濾器類似,可以使用它攔截和修改請求,並且對上游的響應,進行二次處理。過濾器為 org.springframework.cloud.gateway.filter.GatewayFilter 類的實例。
-
Route(路由):
網關配置的基本組成模塊,和 Zuul 的路由配置模塊類似。一個 Route 模塊 由一個 ID,一個目標 URI,一組斷言和一組過濾器定義。如果斷言為真,則路由匹配,目標URI會被訪問。
-
Predicate(斷言):
這是一個 Java 8 的 Predicate,可以使用它來匹配來自 HTTP 請求的任何內容,例如 headers 或參數。斷言的 輸入類型是一個 ServerWebExchange。
1.3 Gateway 處理流程
客戶端向 Spring Cloud Gateway 發出請求,在 Gateway Handler Mapping 中找到與請求相匹配的路由,將其發送到 Gateway Web Handler。Handler 通過指定的過濾器鏈來將請求發送到我們實際的服務執行業務邏輯,然后返回。過濾器之間用虛線分開是因為過濾器可能會在發送代理請求之前(“pre”)或之后(“post”)執行業務邏輯。
二、准備工作
2.1 創建 OrderService 模塊
- 添加依賴
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 編寫配置文件
spring:
application:
# 服務名稱
name: order-service
cloud:
nacos:
discovery:
# 注冊服務中心地址
server-addr: 192.168.205.10:8848
server:
# 設置服務端口
port: 8881
- 創建 OrderController 類
@RestController
@RequestMapping("/order")
public class OrderController {
@RequestMapping("/create")
public String create() {
return "訂單創建成功";
}
}
4) 啟動應用,訪問 http://localhost:8881/order/create ,返回:
訂單創建成功
2.2 創建網關模塊
- 添加依賴
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
三、路由配置方式
3.1 基於配置文件指定URI路由配置方式
spring:
application:
# 配置應用名稱
name: gateway
cloud:
gateway:
routes:
- id: order-service
uri: http://localhost:8881
predicates:
- Path=/order/**
server:
# 配置應用端口
port: 8080
配置說明:
* routes : 表示路由配置,可以存在多個
* id:自定義的路由 ID,保持唯一
* uri:目標服務地址
* predicates::路由條件,Predicate 接受一個輸入參數,返回一個布爾值結果。該接口包含多種默認方法來將 Predicate 組合成其他復雜的邏輯(比如:與,或,非)。
*- Path:基於路徑路由
上面的配置用文字表達如下:
配置一個 Id 為 order-service 的路由規則,當訪問地址以 /order 開頭時,將會把請求轉發到 http://localhost:8881 上去
3.2 基於代碼的路由配置方式
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
public RouteLocator orderRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/order/**")
.uri("http://localhost:8881"))
.build();
}
}
3.3 與注冊中心相結合的路由配置方式
- 添加依賴
<dependencyManagement>
<dependencies>
......
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
- 調整配置
spring:
application:
# 配置應用名稱
name: gateway
cloud:
nacos:
discovery:
# 注冊服務中心地址
server-addr: 192.168.205.10:8848
gateway:
routes:
- id: order-service
# 與單個URL的區別僅僅在於URI的schema協議不同
uri: lb://order-service
predicates:
- Path=/order/**
server:
# 配置應用端口
port: 8080
四、匹配規則
Spring Cloud Gateway 是通過 Spring WebFlux 的 HandlerMapping 做為底層支持來匹配到轉發路由,它內置了很多 Predicates 工廠,這些 Predicates 工廠通過不同的 HTTP 請求參數來匹配,多個 Predicates 工廠可以組合使用。
4.1 Predicate 斷言條件介紹
Predicate 來源於 Java 8,是 Java 8 中引入的一個函數,Predicate 接受一個輸入參數,返回一個布爾值結果。該接口包含多種默認方法來將 Predicate 組合成其他復雜的邏輯(比如:與,或,非)。可以用於接口請求參數校驗、判斷新老數據是否有變化需要進行更新操作。
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性實現了各種路由匹配規則,有通過 Header、請求參數等不同的條件來進行作為條件匹配到對應的路由。下圖總結了 Spring Cloud 內置的幾種 Predicate 的實現:
4.2 匹配方式
方式名稱 | 參數名 | 實例 |
---|---|---|
通過請求參數匹配 | Query | - Query=name,aaa |
通過 Header 屬性匹配 | Header | - Header=X-Request-Id, \d+ |
通過Cookie匹配 | Cookie | - Cookie=sessionId,1001 |
通過 Host 匹配 | Host | - Host=..com |
通過請求方式匹配 | Method | - Method=GET |
通過請求路徑匹配 | Path | - Path=/order/** |
通過請求 ip 地址進行匹配 | RemoteAddr | - RemoteAddr=192.168.1.1/24 |
4.3 通過請求參數匹配實例
Query Route Predicate 支持傳入兩個參數,一個是屬性名一個為屬性值,屬性值可以是正則表達式。
spring:
application:
# 配置應用名稱
name: gateway
cloud:
nacos:
discovery:
# 注冊服務中心地址
server-addr: 192.168.205.10:8848
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Query=name
只有包含屬性名 name 才會轉發,如 http://localhost:8080/order/create?name=aaa。
Query 值可以以鍵值對的方式進行配置:
spring:
application:
# 配置應用名稱
name: gateway
cloud:
nacos:
discovery:
# 注冊服務中心地址
server-addr: 192.168.205.10:8848
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Query=name,aaa
只有當請求中包含屬性名 name 並且值為 aaa 才會轉發 ,http://localhost:8080/order/create?name=aaa。
4.3 通過 Header 屬性匹配實例
spring:
application:
# 配置應用名稱
name: gateway
cloud:
nacos:
discovery:
# 注冊服務中心地址
server-addr: 192.168.205.10:8848
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
# \d 表示匹配數字,+ 表示1 次或多次匹配。
- Header=X-Request-Id, \d+
通過 postman 發起請求,在 header 頭上增加屬性 X-Request-Id ,配置值為任意數值,如 99,即可訪問成功。
五、Filter
路由過濾器允許以某種方式修改傳入的 HTTP 請求或 傳出的 HTTP 響應,路由過濾器適用於特定路由。
5.1 生命周期
SpringCloudGateway 的 Filter 的生命周期不像 Zuul 的那么豐富,它只有兩個:pre 和 post。
- PRE: 過濾器在請求被路由之前調用。可用來實現身份驗證、在集群中選擇請求的微服務、記錄調試信息等。
- POST:過濾器在路由到微服務以后執行。可用來為響應添加標准的 HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
5.3 分類
Spring Cloud Gateway 的 Filter 從作用范圍可分為兩種 GatewayFilter 與 GlobalFilter。
- GatewayFilter:應用到單個路由或者一個分組的路由上。
- GlobalFilter:應用到所有的路由上。
5. 2 GatewayFilter 內置工廠
Spring Cloud Gateway 包括許多內置的 GatewayFilter工廠 :
工廠名稱 | 作用 |
---|---|
AddRequestHeaderGatewayFilterFactory | 添加請求頭的過濾器工廠 |
AddRequestParameterGatewayFilterFactory | 添加請求參數的過濾器工廠 |
AddResponseHeaderGatewayFilterFactory | 添加響應頭的過濾器工廠 |
DedupeResponseHeaderGatewayFilterFactory | 刪除重復響應頭的過濾器工廠 |
HystrixGatewayFilterFactory | Hystrix 過濾器工廠 |
PrefixPathGatewayFilterFactory | 添加前綴路徑的過濾器工廠 |
PreserveHostHeaderGatewayFilterFactory | 保留原請求頭的過濾器工廠 |
RequestRateLimiterGatewayFilterFactory | 保留限流頭的過濾器工廠 |
RedirectToGatewayFilterFactory | 重定向的過濾器工廠 |
RemoveRequestHeaderGatewayFilterFactory | 刪除請求頭的過濾器工廠 |
RemoveResponseHeaderGatewayFilterFactory | 刪除響應頭的過濾器工廠 |
RemoveRequestParameterGatewayFilterFactory | 刪除請求參數的過濾器工廠 |
RewritePathGatewayFilterFactory | 重寫路徑的過濾器工廠 |
RewriteLocationResponseHeaderGatewayFilterFactory | 重寫本地響應頭的過濾器工廠 |
RewriteResponseHeaderGatewayFilterFactory | 重寫響應頭的過濾器工廠 |
SaveSessionGatewayFilterFactory | 保存 session 的過濾器工廠 |
SecureHeadersGatewayFilterFactory | 安全頭的過濾器工廠 |
SetPathGatewayFilterFactory | 設置路徑的過濾器工廠 |
SetRequestHeaderGatewayFilterFactory | 設置請求的過濾器工廠 |
SetResponseHeaderGatewayFilterFactory | 設置響應的過濾器工廠 |
SetStatusGatewayFilterFactory | 設置狀態的過濾器工廠 |
StripPrefixGatewayFilterFactory | StripPrefix過濾器工廠 |
RetryGatewayFilterFactory | 重試過濾器工廠 |
RequestSizeGatewayFilterFactory | 請求大小限制過濾器工廠 |
SetRequestHostGatewayFilterFactory | 設置主機的過濾器工廠 |
5.3 實例之AddRequestHeaderGatewayFilterFactory
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
filters:
- AddRequestHeader=age,18
過濾器工廠會在匹配的請求頭加上一對請求頭,名稱為 age,值為18。
5.4 自定義過濾器
除了使用默認的內置過濾器,我們還可以自己實現過濾器。
- 創建自定義過濾器
public class MyGatewayFilter implements GatewayFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(MyGatewayFilter.class);
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("this is a pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("this is a post filter");
}));
}
/**
* 設定優先級別的,值越大則優先級越低
*
* @return
*/
public int getOrder() {
return 0;
}
}
- 路由配置過濾器
@Bean
public RouteLocator orderRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/order/**")
.filters(f -> f.filter(new MyGatewayFilter()))
.uri("http://localhost:8881")
.order(0)
)
.build();
}
- 啟動項目,訪問 http://localhost:8080/order/create,查看控制台輸出:
2020-09-07 15:23:17.612 INFO 15348 --- [ctor-http-nio-2] c.l.c.gateway.filter.MyGatewayFilter : this is a pre filter
2020-09-07 15:23:18.427 INFO 15348 --- [ctor-http-nio-4] c.l.c.gateway.filter.MyGatewayFilter : this is a post filter
5.5 自定義過濾器工廠
通過自定義過濾器工廠,可以實現在配置文件中配置過濾器。
過濾器工廠的頂級接口是 GatewayFilterFactory ,有2個兩個較接近具體實現的抽象類,分別為AbstractGatewayFilterFactory 和 AbstractNameValueGatewayFilterFactory,前者接收一個參數,比如它的實現類 RedirectToGatewayFilterFactory ;后者接收2個參數,比如它的實現類AddRequestHeaderGatewayFilterFactory 類。