Alibaba微服務元件 - Gateway(二) Spring Cloud Gateway快速開始
2.1 環境搭建
2.1.1 引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2.1.2 編寫yml配置檔案
server: port: 8088 spring: application: name: api-gateway cloud: # 閘道器gateway配置 gateway: # 路由配置 routes: - id: order_route # 路由的唯一標識,路由到order uri: http://localhost:8020 # 需要轉發的地址 # 斷言規則,用於路由規則的匹配 predicates: # 因為是字串所以可以這樣寫,自動對映 # http://localhost:8088/order-serv/order/add 路由到↓ # http://localhost:8020/order-serv/order/add - Path=/order-serv/** # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改 filters: - StripPrefix=1 # 轉發之前去掉1層路徑(內建的一種過濾器) 變成http://localhost:8020/order/add
2.1.3 整合Nacos
引入依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
編寫yml配置檔案
server: port: 8088 spring: application: name: api-gateway cloud: # 閘道器gateway配置 gateway: # 路由配置 routes: - id: order_route # 路由的唯一標識,路由到order uri: lb://order-server # 需要轉發的地址 lb:使用nacos中本地的負載均衡策略 order-server 服務名 # 斷言規則,用於路由規則的匹配 predicates: # 因為是字串所以可以這樣寫,自動對映 # http://localhost:8088/order-serv/order/add 路由到↓ # http://localhost:8020/order-serv/order/add - Path=/order-serv/** # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改 filters: - StripPrefix=1 # 轉發之前去掉1層路徑(內建的一種過濾器) 變成http://localhost:8020/order/add # 註冊到nacos中 nacos: server-addr: 127.0.0.1:8848 username: nacos password: nacos discovery: namespace: 6325e312-7f95-48d7-8d06-810047ed3956
簡寫: 去掉關於路由的配置,自動尋找服務
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
# 閘道器gateway配置
gateway:
discovery:
locator:
enabled: true # 是否啟動我們的自動識別nacos服務 可以直接用服務名作為字首訪問,斷言也是根據服務名做斷言路由到我們的真實服務地址
# http://localhost:8088/order-server/order/add
# 註冊到nacos中
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
discovery:
namespace: 6325e312-7f95-48d7-8d06-810047ed3956
測試(這時候,就發現只要按照閘道器地址/微服務/介面的格式去訪問,就可以得到成功響應)
2.2 路由斷言工廠(Route Predicate Factories)配置(區域性,只針對某一個路由)
作用: 當請求gateway的時候, 使用斷言對請求進行匹配, 如果匹配成功就路由轉發, 如果匹配失敗就返回404
型別:內建,自定義
2.2.1 內建斷言工廠
-
基於Datetime型別的斷言工廠
此型別的斷言根據時間做判斷,主要有三個:
AfterRoutePredicateFactory: 接收一個日期引數,判斷請求日期是否晚於指定日期
BeforeRoutePredicateFactory: 接收一個日期引數,判斷請求日期是否早於指定日期
BetweenRoutePredicateFactory: 接收兩個日期引數,判斷請求日期是否在指定時間段內
ZonedDateTime.now()
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
-
基於遠端地址的斷言工廠
RemoteAddrRoutePredicateFactory:接收一個IP地址段,判斷請求主機地址是否在地址段中‐ RemoteAddr=192.168.1.1/24
-
基於Cookie的斷言工廠
CookieRoutePredicateFactory:接收兩個引數,cookie 名字和一個正則表示式。 判斷請求
cookie是否具有給定名稱且值與正則表示式匹配。‐ Cookie=chocolate, ch.
-
基於Header的斷言工廠
HeaderRoutePredicateFactory:接收兩個引數,標題名稱和正則表示式。 判斷請求Header是否具有給定名稱且值與正則表示式匹配。‐ Header=X‐Request‐Id, \d+
-
基於Host的斷言工廠
HostRoutePredicateFactory:接收一個引數,主機名模式。判斷請求的Host是否滿足匹配規則。‐ Host=**.testhost.org
-
基於Method請求方法的斷言工廠
MethodRoutePredicateFactory:接收一個引數,判斷請求型別是否跟指定的型別匹配。‐ Method=GET
-
基於Path請求路徑的斷言工廠
PathRoutePredicateFactory:接收一個引數,判斷請求的URI部分是否滿足路徑規則。‐ Path=/foo/{segment}
-
基於Query請求引數的斷言工廠
QueryRoutePredicateFactory :接收兩個引數,請求param和正則表示式, 判斷請求引數是否具有給定名稱且值與正則表示式匹配。- Query=baz, ba.
-
基於路由權重的斷言工廠
WeightRoutePredicateFactory:接收一個[組名,權重], 然後對於同一個組內的路由按照權重轉發
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
2.2.2 自定義斷言工廠
1. 必須spring元件 注入bean(@Component)
2. 類必須加上RoutePredicateFactory作為結尾
3. 必須繼承AbstractRoutePredicateFactory
4. 必須宣告靜態內部類 宣告屬性來接收 配置檔案中對應的斷言的資訊
5. 需要結合shortcutFieldOrder進行繫結
6. 通過apply進行邏輯判斷 true就是匹配成功 false匹配失敗
注意: 命名需要以 RoutePredicateFactory 結尾,必須注入到spring中
package com.xiexie.gateway.predicate;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
* @Description 自定義斷言工廠
* @Date 2022-04-19 13:49
* @Author xie
*/
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {
public CheckAuthRoutePredicateFactory() {
super(CheckAuthRoutePredicateFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
// 必須跟config中的屬性名進行繫結
return Arrays.asList("name");
}
@Override
public Predicate<ServerWebExchange> apply(CheckAuthRoutePredicateFactory.Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
if (StringUtils.isEmpty(config.getName()) || !"xiexie".equals(config.getName())) {
return false;
}
return true;
}
@Override
public String toString() {
return String.format("CheckAuth: name=%s", config.getName());
}
};
}
/**
* 用於接收配置檔案中 斷言的資訊
*/
@Validated
public static class Config {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
# 閘道器gateway配置
gateway:
# 路由配置
routes:
- id: order_route # 路由的唯一標識,路由到order
uri: lb://order-server # 需要轉發的地址 lb:使用nacos中本地的負載均衡策略 order-server 服務名
# 斷言規則,用於路由規則的匹配
predicates:
# 因為是字串所以可以這樣寫,自動對映
# http://localhost:8088/order-serv/order/add 路由到↓
# http://localhost:8020/order-serv/order/add
- Path=/order-serv/**
# 自定義CheckAuth斷言工廠
- CheckAuth=xiexie
# 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
filters:
- StripPrefix=1 # 轉發之前去掉1層路徑(內建的一種過濾器) 變成http://localhost:8020/order/add
# 註冊到nacos中
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
discovery:
namespace: 6325e312-7f95-48d7-8d06-810047ed3956
2.3 過濾器工廠( GatewayFilter Factories)配置(區域性,只針對某一個路由)
Gateway 內建了很多的過濾器工廠,我們通過一些過濾器工廠可以進行一些業務邏輯處理器,比如新增剔除響應頭,新增去除引數等
官方文件:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
2.3.1 內建過濾器工廠
過濾器工廠 | 作用 | 引數 |
---|---|---|
AddRequestHeader | 為原始請求新增Header | Header的名稱及值 |
AddRequestParameter | 為原始請求新增請求引數 | 引數名稱及值 |
AddResponseHeader | 為原始響應新增Header | Header的名稱及值 |
DedupeResponseHeader | 剔除響應頭中重複的值 | 需要去重的Header名稱及去重策略 |
Hystrix | 為路由引入Hystrix的斷路器保護 | HystrixCommand的名稱 |
FallbackHeaders | 為fallbackUri的請求頭中新增具體的異常資訊 | Header的名稱 |
PrefixPath | 為原始請求路徑新增字首 | 字首路徑 |
PreserveHostHeader | 為請求新增一個preserveHostHeader=true 的屬性,路由過濾器會檢查該屬性以決定是否要傳送原始的Host | 無 |
RequestRateLimiter | 用於對請求限流,限流演算法為令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
RedirectTo | 將原始請求重定向到指定的URL | http狀態碼及重定向的url |
RemoveHopByHopHeadersFilter | 為原始請求刪除IETF組織規定的一系列Header | 預設就會啟用,可以通過配置指定僅刪除哪些Header |
RemoveRequestHeader | 為原始請求刪除某個Header | Header名稱 |
RemoveResponseHeader | 為原始響應刪除某個Header | Header名稱 |
RewritePath | 重寫原始的請求路徑 | 原始路徑正則表示式以及重寫後路徑的正則表示式 |
RewriteResponseHeader | 重寫原始響應中的某個Header | Header名稱、值的正則表示式,重寫後的值 |
SaveSession | 在轉發請求之前,強制執行WebSession::save操作 | 無 |
secureHeaders | 為原始響應新增一系列起安全作用的響應頭 | 無,支援修改這些安全響應頭的值 |
SetPath | 修改原始的請求路徑 | 修改後的路徑 |
SetResponseHeader | 修改原始響應中某個Header的值 | Header名稱,修改後的值 |
SetStatus | 修改原始響應的狀態碼 | HTTP 狀態碼,可以是數字,也可以是字串 |
StripPrefix | 用於截斷原始請求的路徑 | 使用數字表示要截斷的路徑的數量 |
Retry | 針對不同的響應進行重試 | retries、statuses、methods、series |
RequestSize | 設定允許接收最大請求包的大 小。如果請求包大小超過設定的值,則返回 413 Payload Too Large | 請求包大小,單位為位元組,預設值為5M |
ModifyRequestBody | 在轉發請求之前修改原始請求體內容 | 修改後的請求體內容 |
ModifyResponseBody | 修改原始響應體的內容 | 修改後的響應體內容 |
2.3.1.1 新增請求頭(上下文參考-全量配置檔案)
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
# 閘道器gateway配置
gateway:
# 路由配置
routes:
- id: order_route # 路由的唯一標識,路由到order
uri: lb://order-server # 需要轉發的地址 lb:使用nacos中本地的負載均衡策略(loadbalancer) order-server 服務名
# 斷言規則,用於路由規則的匹配
predicates:
# 因為是字串所以可以這樣寫,自動對映
# http://localhost:8088/order-serv/order/add 路由到↓
# http://localhost:8020/order-serv/order/add http://order-server/order-serv/order/add
- Path=/order-serv/**
# 自定義CheckAuth斷言工廠
- CheckAuth=xiexie # 資料放在自定義斷言工廠的config中,根據config對映到的資料值進行判斷是否匹配到
# 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
filters:
- StripPrefix=1 # 轉發之前去掉1層路徑(內建的一種過濾器) 變成http://localhost:8020/order/add
# 其他的內建過濾器
- AddRequestHeader=X-Request-color, red # 新增請求頭引數
# - AddRequestParameter=color, blue # 新增請求引數
# - PrefixPath=/mall‐order # 新增字首 對應微服務需要配置context‐path: /mall‐order
# - RedirectTo=302, https://www.baidu.com/ # 重定向到百度
# - CheckAuth=name, xiexie # 資料放在自定義的過濾器中,根據config對映到的資料進行判斷是否放行
# 註冊到nacos中
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
discovery:
namespace: 6325e312-7f95-48d7-8d06-810047ed3956
@RequestMapping("/header")
public String header(@RequestHeader("X-Request-color") String color) {
System.out.println("gateWay獲取請求頭X‐Request‐color:" + color);
return color;
}
2.3.1.2 新增請求引數(上下文參考上文全量配置檔案)
- AddRequestParameter=color, blue # 新增請求引數
@RequestMapping("/parameter")
public String parameter(@RequestParam("color") String color) {
System.out.println("gateWay獲取引數color:" + color);
return color;
}
2.3.1.3 為匹配的路由統一新增字首(上下文參考上文全量配置檔案)
- PrefixPath=/mall‐order # 新增字首 對應微服務需要配置context‐path: /mall‐order
server:
port: 8020
servlet:
context-path: /mall‐order
2.3.1.4 重定向操作(上下文參考上文全量配置檔案)
- RedirectTo=302, https://www.baidu.com/ # 重定向到百度
.....
.....
還有很多,自行參照上面列表測試
2.3.2 自定義過濾器工廠
繼承AbstractNameValueGatewayFilterFactory且我們的自定義名稱必須要以GatewayFilterFactory結尾並交給spring管理。
跟自定義的斷言工廠相似
實現過濾工廠
package com.xiexie.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* @Description
* @Date 2022-04-19 15:37
* @Author xie
*/
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {
public CheckAuthGatewayFilterFactory() {
super(CheckAuthGatewayFilterFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("param", "value");
}
@Override
public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
Set<String> stringSet = queryParams.keySet();
if (stringSet.contains(config.getParam()) && config.getValue().equals(queryParams.get(config.getParam()).get(0))) {
// 正常請求
return chain.filter(exchange);
} else {
// 返回404並結束
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
return exchange.getResponse().setComplete();
}
}
};
}
public static class Config {
private String param;
private String value;
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
配置檔案中配置(上下文參考上文全量配置檔案)
- CheckAuth=name, xiexie # 資料放在自定義的過濾器中,根據config對映到的資料進行判斷是否放行
2.4 全域性過濾器(Global Filters)配置
區域性過濾器和全域性過濾器區別:
- 區域性:區域性針對某個路由, 需要在路由中進行配置
- 全域性:針對所有路由請求, 一旦定義就會投入使用
GlobalFilter 介面和 GatewayFilter 有一樣的介面定義,只不過, GlobalFilter 會作用於所有路由
2.4.1 自定義全域性過濾器
package com.xiexie.gateway.filter.global;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Description 自定義全域性過濾器
* @Date 2022-04-19 16:30
* @Author xie
*/
@Component
public class GlobalLogFilter implements GlobalFilter {
Logger log = LoggerFactory.getLogger(GlobalLogFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info(exchange.getRequest().getPath().value());
return chain.filter(exchange);
}
}
2.4.2 Reactor Netty 訪問日誌(gateway自動定義的日誌收集)
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#reactor-netty-access-logs
要啟用 Reactor Netty 訪問日誌,請設定 -Dreactor.netty.http.server.accessLogEnabled=true
它必須是 Java 系統屬性(設定JVM環境變數的),而不是 Spring Boot 屬性。
java -jar xxxGatewat.jar -Dreactor.netty.http.server.accessLogEnabled=true
可以將日誌記錄系統配置為具有單獨的訪問日誌檔案。(比如配置在Logback)
2.5 Gateway跨域配置(CORS Configuration)
通過yml配置的方式
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration
2.5.1 通過yml配置的方式
server:
port: 8088
spring:
application:
name: api-gateway
cloud:
# 閘道器gateway配置
gateway:
# 跨域配置
globalcors:
cors-configurations:
'[/**]': # 允許跨域訪問的資源
allowedOrigins: "*" # 跨域允許的來源
allowedMethods: # 跨域允許的method
- GET
- POST
- DELETE
- PUT
- OPTION
2.5.1 通過java程式碼方式
package com.xiexie.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* @Description 跨域配置
* @Date 2022-04-19 17:06
* @Author xie
*/
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*"); // 允許跨域的method
config.addAllowedOrigin("*"); // 允許跨域的來源
config.addAllowedHeader("*"); // 允許跨域的header頭
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config); // 允許跨域訪問的資源
return new CorsWebFilter(source);
}
}