Spring cloud gateway 使用

語言: CN / TW / HK

這是我參與11月更文挑戰的第4天,活動詳情檢視:2021最後一次更文挑戰

基本術語

  • Route(路由) :這是閘道器的基本構建塊。它由一個 ID,一個目標 URI,一組斷言和一組過濾器定義。如果斷言為真,則路由匹配。
  • Predicate(斷言) :這是一個 Java 8 的 Predicate。輸入型別是一個 ServerWebExchange。我們可以使用它來匹配來自 HTTP 請求的任何內容,例如 headers 或引數。
  • Filter(過濾器) :這是org.springframework.cloud.gateway.filter.GatewayFilter的例項,我們可以使用它修改請求和響應。

斷言 Predicate

Predicate(斷言, 謂詞) 用於進行條件判斷,只有斷言都返回真,才會真正的執行路由。

斷言就是說: 在什麼條件下才能進行路由轉發。

在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性實現了各種路由匹配規則,有通過 Header、請求引數等不同的條件來進行作為條件匹配到對應的路由。

img

BetweenRoutePredicateFactory為例,使用方法如下:

application.yml

spring:     cloud:         gateway:                 routes:                 - id: between_route                     uri: http://www.google.com                     predicates:                     - Between=2018-12-25T14:33:47.789+08:00, 2018-12-26T14:33:47.789+08:00

斷言除了以上常見用法外,還有一方法ReadBodyPredicateFactory用於獲取請求體Request Body資訊,此方法沒有被記錄於官方文件,但在2.2.6RELEASE版本可用。

使用方法如下:

@Bean    public RouteLocator routes(RouteLocatorBuilder builder) {        return builder.routes()               .route("test", r -> r.path("/test/path/**")                       .and()                       .readBody(String.class, requestBody -> true)                       .filters(f -> f.filter(testGatewayFilterFactory.apply(new TestFactory.Config())))                       .uri("lb://test"))               .build();   }

該方法通過裝飾器模式,為exchange新增一個attributecachedRequestBodyObject 的方式,來實現獲取請求體資訊。(詳見程式碼ServerWebExchangeUtils中的cacheRequestBody方法)

通過該斷言處理後,直接獲取exchangecachedRequestBodyObject即可獲取到請求體資訊。

String body = exchange.getAttribute("cachedRequestBodyObject");

通過網上相關資訊可知,目前已知缺陷主要有兩點:

1、可能只能通過Java code的方式進行配置(未找到有效的yaml配置方法)。

2、當使用ReadBodyPredicateFactory讀取request的body,可能無法匹配404。(詳見CSDN

過濾器 filter

過濾器工廠 GatewayFilter Factories

Spring Cloud Gateway的路由過濾器允許以某種方式修改傳入的HTTP請求或輸出的HTTP響應。只作用於特定的路由。Spring Cloud Gateway中內建了很多的過濾器工廠,以便我們能夠快速地實現相應常見的功能。

現已支援30種過濾器工廠,詳見官方文件

過濾器工廠的使用方法有兩種,一種是通過yaml的方式進行配置與使用,一種是通過Java code的方式進行配置與使用。

AddRequestHeader GatewayFilter factory為例,該工廠接受namevalue兩個引數,從而為請求添加了Request Header

使用方法如下:

application.yml

spring: cloud:   gateway:     routes:     - id: add_request_header_route       uri: https://example.org       predicates:       - Path=/red/{segment}       filters:       - AddRequestHeader=X-Request-Red, Blue-{segment}

ModifyRequestBody filter為例,該工廠在閘道器過濾器層面,修改請求體內容。

使用方法如下:

@Bean public RouteLocator routes(RouteLocatorBuilder builder) {    return builder.routes()       .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")           .filters(f -> f.prefixPath("/httpbin")               .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,                   (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))       .build(); } ​ static class Hello {    String message; ​    public Hello() { } ​    public Hello(String message) {        this.message = message;   } ​    public String getMessage() {        return message;   } ​    public void setMessage(String message) {        this.message = message;   } }

全域性過濾器 GlobalFilter

當請求匹配路由後,就會進入過濾器的執行鏈filter chainfilter chain包括全域性過濾器GlobalFilter,還有設定了過濾某個介面的區域性過濾器GatewayFilter

全域性過濾器GlobalFilter,顧名思義就是對所有匹配路由成功後的請求進行過濾。全域性過濾器需要實現GlobalFilterOrdered兩個介面,同時加上註解@Component即可配置成功。其中,GlobalFilter代表全域性過濾器,Ordered代表優先順序,數字越小,優先順序越大。

具體用法如下:

@Component public class CustomGlobalFilter implements GlobalFilter, Ordered { ​    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        log.info("custom global filter");        return chain.filter(exchange);   } ​    @Override    public int getOrder() {        return -1;   } }

區域性過濾器 GatewayFilter

區域性過濾器 GatewayFilter就是對指定匹配路由成功後的請求進行過濾。區域性過濾器需要實現GatewayFilterOrdered兩個介面,同時需要編寫相應的工廠類生產區域性過濾器(工廠類的名字需要符合XXGatewayFilterFactory的格式),最後將工廠類通過yaml或者Java code的方式進行配置即可。

具體用法如下:

區域性過濾器:

@Component public class TestFilter implements GatewayFilter, Ordered { ​    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        return chain.filter(exchange);   } ​    @Override    public int getOrder() {        return 0;   } }

區域性過濾器工廠:

@Component public class TestGatewayFilterFactory extends        AbstractGatewayFilterFactory<CookieGatewayFilterFactory.Config> { ​ ​    @Autowired    TestGatewayFilter testGatewayFilter; ​ ​    public TestGatewayFilterFactory() {        super(TestGatewayFilterFactory.Config.class);   } ​    @Override    public GatewayFilter apply(TestGatewayFilterFactory.Config config) {        return testGatewayFilter;   } ​    public static class Config {   } }

yaml配置:

- id: test123         uri: lb://test123         predicates:           - Path=/test/**         filters:           - Test  #只需填寫XXGatewayFilterFactory中的XX即可識別

Java code配置:

@Autowired    private CookieGatewayFilterFactory cookieGatewayFilterFactory; ​ @Bean    public RouteLocator routes(RouteLocatorBuilder builder) {        return builder.routes()               .route("test", r -> r.path("/test/path/**")                       .filters(f -> f.filter(testGatewayFilterFactory.apply(new TestFactory.Config())))                       .uri("lb://test"))               .build();   }