目錄
- 介紹
- 核心功能
- 三大核心
- Route以服務名動態獲取URL
- Predicate常用斷言
- Path Route Predicate
- After Route Predicate
- Before Route Predicate
- Between Route Predicate
- Cookie Route Predicate
- Header Route Predicate
- Host Route Predicate
- Query Route Predicate
- RemoteAddr Route Predicate
- Method Route Predicate
- 自定義Predicate
- 自定義Filter
- 自定義全局Filter統計接口調用耗時情況
- 自定義條件Filter
- 總結
介紹
Spring Cloud Gateway 是 Spring Cloud 生態系統中的一個基于 Spring WebFlux 的 API 網關解決方案,旨在為微服務架構提供統一的入口點,支持路由、過濾、負載均衡等功能。它通過非阻塞的響應式編程模型,提供了高效、靈活的 API 網關能力。
核心功能
- 動態路由: 支持基于路徑、Header、Query參數等規則的動態路由。
- 過濾器鏈: 通過全局過濾器 (Global Filter) 和局部過濾器 (Gateway Filter) 實現請求增強與響應修改。
- 負載均衡: 集成Spring Cloud LoadBalancer,支持多實例流量分發。
- 限流與熔斷: 內置限流功能,支持 Redis 令牌桶算法,可結合 Resilience4j 實現熔斷。
- 安全與監控: 支持JWT、OAuth2等認證機制,保護后端服務。
三大核心
1. 路由 (Route)
路由是網關的核心,用于定義請求的轉發規則。他由ID、目標URL、一系列的斷言和過濾器組成,如果斷言為true則路由匹配成功。一個路由包含以下屬性:
- ID: 路由的唯一標識。
- URI: 目標服務的地址,支持 http、lb(負載均衡)等協議。
- 斷言: 定義路由匹配條件,如路徑、Header、方法等。
- 過濾器: 對請求或響應進行處理的邏輯。
2. 斷言 (Predicate)
斷言用于判斷請求是否符合路由規則。常見的斷言包括:
- Path: 路徑匹配。
- Method: HTTP 方法匹配。
- Header: 請求頭匹配。
- Query: 請求參數匹配。
- After、Before、Between: 時間范圍匹配。
3. 過濾 (Filter)
過濾器用于在請求轉發前或響應返回后執行特定邏輯。常見的過濾器包括:
- 全局過濾器: 對所有路由生效,如日志記錄、認證校驗。
- 局部過濾器: 僅對特定路由生效,如路徑重寫、請求限流。
Route以服務名動態獲取URL
網關服務 cloud-gateway9527 引入依賴
<!-- gateway -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 服務注冊發現consul discovery,網關也要注冊進服務注冊中心統一管控 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-discovery</artifactId><exclusions><exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion></exclusions>
</dependency>
<!-- 指標監控健康檢查的actuator -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
yml配置
server:port: 9527spring:application:name: cloud-gateway# Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由
訂單服務 cloud-feign-order9002:OrderGatewayController
@RestController
public class OrderGatewayController {@Resourceprivate PayFeignApi payFeignApi;@GetMapping("/feign/pay/gateway/get/{id}")public Result getById(@PathVariable("id") Integer id) {return payFeignApi.getById(id);}
}
Feign接口 cloud-common-api:PayFeignApi
@FeignClient("cloud-gateway")
public interface PayFeignApi {@GetMapping("/pay/gateway/get/{id}")public Result getById(@PathVariable("id") Integer id);
}
支付服務 cloud-payment8001:PayGatewayController
@RestController
public class PayGatewayController {@Resourceprivate PayService payService;@GetMapping(value = "/pay/gateway/get/{id}")public Result<Pay> getById(@PathVariable("id") Integer id) {Pay pay = payService.getById(id);return Result.success(pay);}
}
測試結果
啟動支付服務8001,啟動訂單服務9002,訪問 http://localhost:9002/feign/pay/gateway/get/1 返回異常。再啟動網關服務9527,訪問 http://localhost:9002/feign/pay/gateway/get/1 返回成功。
Predicate常用斷言
Path Route Predicate
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由
測試結果
訪問 http://localhost:9527/pay/gateway/get/1 返回成功。
After Route Predicate
# 獲取當前時間串
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- After=2025-04-19T15:51:15.516781+08:00[Asia/Shanghai] # 在某個時間之后可以訪問
測試結果
2025-04-19 15:51:15 之前訪問 http://localhost:9527/pay/gateway/get/1 返回失敗。
2025-04-19 15:51:15 之后訪問 http://localhost:9527/pay/gateway/get/1 返回成功。
Before Route Predicate
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- Before=2025-04-19T15:51:15.516781+08:00[Asia/Shanghai] # 在某個時間之前可以訪問
測試結果
2025-04-19 15:51:15 之前訪問 http://localhost:9527/pay/gateway/get/1 返回成功。
2025-04-19 15:51:15 之后訪問 http://localhost:9527/pay/gateway/get/1 返回失敗。
Between Route Predicate
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- Between=2025-04-19T16:11:15.516781+08:00[Asia/Shanghai],2025-04-19T16:12:15.516781+08:00[Asia/Shanghai] # 在某個時間段之間可以訪問
測試結果
2025-04-19 16:11:15 到 2025-04-19 16:12:15 時間段之間訪問 http://localhost:9527/pay/gateway/get/1 返回成功。
Cookie Route Predicate
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- Cookie=username,zzyy # cookie中含有username=zzyy可以訪問
測試結果
- 原生命令測試
curl http://localhost:9527/pay/gateway/get/1 --cookie “username=zzyy” 返回成功。 - postman測試
請求頭中添加 cookie:username=zzyy,訪問 http://localhost:9527/pay/gateway/get/1 返回成功。
Header Route Predicate
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- Header=X-Request-Id,\d+ # 請求頭中含有X-Request-Id且值為整數的正則表達式可以訪問
測試結果
- 原生命令測試
curl http://localhost:9527/pay/gateway/get/1 -H “X-Request-Id:123” 返回成功。 - postman測試
請求頭中添加 X-Request-Id:123,訪問 http://localhost:9527/pay/gateway/get/1 返回成功。
Host Route Predicate
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- Host=**.zzyy.com # 主機地址后必須含有.zzyy.com可以訪問
測試結果
- 原生命令測試
curl http://localhost:9527/pay/gateway/get/1 -H “Host:www.zzyy.com” 返回成功。 - postman測試
請求頭中添加 Host:www.zzyy.com,訪問 http://localhost:9527/pay/gateway/get/1 返回成功。
Query Route Predicate
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- Query=uid,\d+ # 請求參數必須含有uid且值為整數的正則表達式可以訪問
測試結果
訪問 http://localhost:9527/pay/gateway/get/1?uid=123 返回成功。
RemoteAddr Route Predicate
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- RemoteAddr=192.168.42.1/24 # 遠程訪問地址必須是192.168.42.xx才能訪問
測試結果
當前電腦IP為 192.168.42.3,訪問 http://192.168.42.3:9527/pay/gateway/get/1 返回成功。
Method Route Predicate
spring:cloud:gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- Method=GET,POST # get/post請求可以訪問
測試結果
get 請求訪問 http://localhost:9527/pay/gateway/get/1 返回成功。
自定義Predicate
網關服務 cloud-gateway9527 中新建 MyRoutePredicateFactory
// 自定義配置會員等級,按照 鉑、金、銀和yml配置的會員等級,才可以訪問
// 繼承 AbstractRoutePredicateFactory
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {// 無參構造方法public MyRoutePredicateFactory() {super(MyRoutePredicateFactory.Config.class);}// 短格式public List<String> shortcutFieldOrder() {return Collections.singletonList("userType");}// 重寫apply方法@Overridepublic Predicate<ServerWebExchange> apply(Config config) {return new Predicate<ServerWebExchange>() {@Overridepublic boolean test(ServerWebExchange serverWebExchange) {// 檢查request參數中是否存在userType,且值和config中的相同,則可以訪問String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");return userType != null && userType.equals(config.getUserType());}};}// 這個Config類就是路由斷言規則public static class Config {private @NotNull String userType; // 鉑、金、銀和yml配置的會員等級public Config() {}public String getUserType() {return userType;}public void setUserType(String userType) {this.userType = userType;}}
}
yml配置
server:port: 9527spring:application:name: cloud-gateway# Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由- My=gold # 自定義斷言
測試結果
訪問 http://localhost:9527/pay/gateway/get/1?userType=gold 返回成功。
自定義Filter
自定義全局Filter統計接口調用耗時情況
網關服務 cloud-gateway9527 中新建 MyGlobalFilter
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {private static final String START_TIME = "start_time"; // 開始調用方法的時間@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 記錄開始時間exchange.getAttributes().put(START_TIME, System.currentTimeMillis());return chain.filter(exchange).then(Mono.fromRunnable(() -> {Long startTime = exchange.getAttribute(START_TIME);if (startTime != null) {URI uri = exchange.getRequest().getURI();log.info("訪問接口主機:" + uri.getHost() + ",端口:" + uri.getPort() +",URL:" + uri.getPath() + ",參數:" + uri.getRawQuery() +",時長:" + (System.currentTimeMillis() - startTime) + "毫秒");}}));}// 值越小,優先級越高@Overridepublic int getOrder() {return 0;}
}
測試結果
訪問 http://localhost:9527/pay/gateway/get/1 日志輸出:訪問接口主機:localhost,端口:9527,URL:/pay/gateway/get/1,參數:null,時長:6毫秒
自定義條件Filter
// 繼承 AbstractGatewayFilterFactory
@Component
@Slf4j
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {// 無參構造方法public MyGatewayFilterFactory() {super(MyGatewayFilterFactory.Config.class);}// 短格式public List<String> shortcutFieldOrder() {return Arrays.asList("state");}// 重寫apply方法@Overridepublic GatewayFilter apply(MyGatewayFilterFactory.Config config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();log.info("進入自定義條件過濾器MyGatewayFilterFactory, state=" + config.getState());if (request.getQueryParams().containsKey("zzyy")) {return chain.filter(exchange);}exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);return exchange.getResponse().setComplete();}};}public static class Config {private String state;public Config() {}public String getState() {return state;}public void setState(String state) {this.state = state;}}
}
yml配置
server:port: 9527spring:application:name: cloud-gateway# Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}gateway:routes:- id: pay_routh1 # pay_routh1 (路由的ID,沒有固定規則但要求唯一,建議配合服務名)uri: lb://cloud-payment-service # 服務名predicates:- Path=/pay/gateway/get/** # 斷言,路徑匹配的進行路由filters:- My=zzyy # 自定義條件過濾器
測試結果
訪問 http://localhost:9527/pay/gateway/get/1?zzyy=16 返回成功。
總結
以上主要介紹了 Spring Cloud Gateway 路由、斷言、過濾的相關知識,以及自定義 Predicate 和 Filter,想了解更多 Spring Cloud Gateway 知識的小伙伴請參考 Spring Cloud Gateway 官網 進行學習,學習更多 Spring Cloud 實戰實用技巧的小伙伴,請關注后期發布的文章,認真看完一定能讓你有所收獲。