spring cloud gateway 不生效

zhaozhangxiao發表於2022-05-07

要說到gateway閘道器過濾,那一定要明白-過濾器-自定義區域性、全域性過濾器、區別。

前兩天除錯gateway閘道器問題,發現GatewayFilter和GlobalFilter使用錯了,導致導致閘道器攔截失效,搞了一上午才找到問題。

spring cloud gateway 不生效

 自定義過濾器需要實現GatewayFilterOrdered。其中GatewayFilter中的這個方法就是用來實現你的自定義的邏輯的

Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

示例:統計某個服務的響應時間

1.1、建立Filer

複製程式碼

public class ElapsedFilter implements GatewayFilter, Ordered { private static final Log log = LogFactory.getLog(GatewayFilter.class); private static final String ELAPSED_TIME_BEGIN = "elapsedTimeBegin";

    @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(ELAPSED_TIME_BEGIN, System.currentTimeMillis()); return chain.filter(exchange).then(
                Mono.fromRunnable(() -> {
                    Long startTime = exchange.getAttribute(ELAPSED_TIME_BEGIN); if (startTime != null) {
                        log.info(exchange.getRequest().getURI().getRawPath() + ": " + (System.currentTimeMillis() - startTime) + "ms");
                    }
                })
        );
    }

    @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE;
    }
}

複製程式碼

在請求剛剛到達時,往ServerWebExchange中放入了一個屬性elapsedTimeBegin,屬性值為當時的毫秒級時間戳。然後在請求執行結束後,又從中取出我們之前放進去的那個時間戳,與當前時間的差值即為該請求的耗時。因為這是與業務無關的日誌所以將Ordered設為Integer.MAX_VALUE以降低優先順序。

chain.filter(exchange)之前的就是 “pre” 部分,之後的也就是then裡邊的是 “post” 部分。

1.2、配置

建立好 Filter 之後我們將它新增到我們的 Filter Chain 裡邊

複製程式碼

@Bean public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) { // @formatter:off
    return builder.routes()
            .route(r -> r.path("/fluent/customer/**")
                         .filters(f -> f.stripPrefix(2)
                                        .filter(new ElapsedFilter())
                                        .addResponseHeader("X-Response-Default-Foo", "Default-Bar"))
                         .uri("lb://CONSUMER")
                         .order(0)
                         .id("fluent_customer_service")
            )
            .build(); // @formatter:on
}

複製程式碼

注意:實際在使用 Spring Cloud 的過程中,需要使用 Sleuth+Zipkin 來進行耗時分析。

二、全域性過濾器

有多個路由就需要一個一個來配置,並不能透過像下面這樣來實現全域性有效(也未在 Fluent Java API 中找到能設定 defaultFilters 的方法)

@Bean public ElapsedFilter elapsedFilter(){ return new ElapsedFilter();
}

自定義過濾器需要實現GlobalFilter和Ordered

示例:校驗token

2.1、建立Filter

複製程式碼

public class TokenFilter implements GlobalFilter, Ordered {

    @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token"); if (token == null || token.isEmpty()) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete();
        } return chain.filter(exchange);
    }

    @Override public int getOrder() { return -100;
    }
}

複製程式碼

2.2、在 Spring Config 中配置這個 Bean

@Bean public TokenFilter tokenFilter(){ return new TokenFilter();
}

三、GatewayFilter與GlobalFilter的區別

3.1、GatewayFilter

  在一個高的角度來看,Global filters會被應用到所有的路由上,而Gateway filter將應用到單個路由上或者一個分組的路由上。

  GatewayFilter是從WebFilter中Copy過來的,相當於一個Filter過濾器,可以對訪問的URL過濾橫切處理,應用場景比如超時,安全等。

  GatewayFilter和GlobalFilter兩個介面中定義的方法一樣都是Mono filter(ServerWebExchange exchange, GatewayFilterChain chain),唯一的區別就是GatewayFilter繼承了ShortcutConfigurable,GlobalFilter沒有任何繼承。

  參看示例一:Gateway Filter與RouteLocator繫結使用

3.2、GlobalFilter

  Spring Cloud gateway定義了GlobalFilter的介面讓我們去自定義實現自己的的GlobalFilter。GlobalFilter是一個全域性的Filter,作用於所有的路由。

  讓其在Gateway中執行生效,有兩種方式一種直接加[@Component](https://github.com/Component "@Component")註解,另外一種可以在 Spring Config 中配置這個 Bean如下示例二所示;

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章