009-spring cloud gateway-過濾器GatewayFilter、GlobalFilter、GatewayFilterChain、做用、生命週期、GatewayFilterFacto

1、概述

  在Spring-Cloud-Gateway之請求處理流程中最終網關是將請求交給過濾器鏈表進行處理。html

  核心接口:GatewayFilter,GlobalFilter,GatewayFilterChain。spring

查看總體類圖編程

  

2、網關過濾器做用

  

  當使用微服務構建整個 API 服務時,通常有許多不一樣的應用在運行,如上圖所示的mst-user-servicemst-good-servicemst-order-service,這些服務都須要對客戶端的請求的進行 Authentication。最簡單粗暴的方法就是像上圖同樣,爲每一個微服務應用都實現一套用於校驗的過濾器或攔截器。安全

  經過前置的網關服務來完成這些非業務性質的校驗。app

  

3、Filter 的生命週期

  Spring Cloud Gateway 的 Filter 的生命週期有兩個:「pre」 和 「post」。負載均衡

  

  「pre」和 「post」 分別會在請求被執行前調用和被執行後調用,和 Zuul Filter 或 Spring Interceptor 中相關生命週期相似,但在形式上有些不同。ide

  Zuul 的 Filter 是經過filterType()方法來指定,一個 Filter 只能對應一種類型,要麼是 「pre」 要麼是「post」。Spring Interceptor 是經過重寫HandlerInterceptor中的三個方法來實現的。而 Spring Cloud Gateway 基於 Project Reactor 和 WebFlux,採用響應式編程風格,打開它的 Filter 的接口GatewayFilter你會發現它只有一個方法filter函數

4、核心接口解讀

4.一、GatewayFilterChain--網關過濾鏈表

/**
 * 網關過濾鏈表接口
 * 用於過濾器的鏈式調用
 */
public interface GatewayFilterChain {

    /**
     *  鏈表啓動調用入口方法*/
    Mono<Void> filter(ServerWebExchange exchange);

}

默認實現微服務

  /**
     * 網關過濾的鏈表,用於過濾器的鏈式調用
     * 過濾器鏈表接口的默認實現,
     * 包含2個構建函數:
     *  1.集合參數構建用於初始化吧構建鏈表
     *  2. index,parent參數用於構建當前執行過濾對應的下次執行的鏈表 
     */
    private static class DefaultGatewayFilterChain implements GatewayFilterChain {

        /**
         * 當前過濾執行過濾器在集合中索引
         */
        private final int index;
        /**
         * 過濾器集合
         */
        private final List<GatewayFilter> filters;

        public DefaultGatewayFilterChain(List<GatewayFilter> filters) {
            this.filters = filters;
            this.index = 0;
        }

        /**
         * 構建
         * @param parent 上一個執行過濾器對應的FilterChain
         * @param index  當前要執行過濾器的索引
         */
        private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
            this.filters = parent.getFilters();
            this.index = index;
        }

        public List<GatewayFilter> getFilters() {
            return filters;
        }

        /**
         * @param exchange the current server exchange
         * @return
         */
        @Override
        public Mono<Void> filter(ServerWebExchange exchange) {
            return Mono.defer(() -> {
                if (this.index < filters.size()) {
                    //獲取當前索引的過濾器
                    GatewayFilter filter = filters.get(this.index);
                    //構建當前索引的下一個過濾器的FilterChain
                    DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);
                    //調用過濾器的filter方法執行過濾器
                    return filter.filter(exchange, chain);
                } else {
                    //當前索引大於等於過濾集合大小,標識全部鏈表都已執行完畢,返回空
                    return Mono.empty(); // complete
                }
            });
        }
    }

過濾器的GatewayFilterChain 執行順序post

  1. 經過GatewayFilter集合構建頂層的GatewayFilterChain
  2. 調用頂層GatewayFilterChain,獲取第一個Filter,並建立下一個Filter索引對應的GatewayFilterChain
  3. 調用filter的filter方法執行當前filter,並將下次要執行的filter對應GatewayFilterChain傳入。

4.二、GatewayFilter--網關路由過濾器

/**
 * 網關路由過濾器,
 * Contract for interception-style, chained processing of Web requests that may
 * be used to implement cross-cutting, application-agnostic requirements such
 * as security, timeouts, and others. Specific to a Gateway
 *
 */
public interface GatewayFilter extends ShortcutConfigurable {

    String NAME_KEY = "name";
    String VALUE_KEY = "value";

    /**
     *  過濾器執行方法
     * Process the Web request and (optionally) delegate to the next
     * {@code WebFilter} through the given {@link GatewayFilterChain}.
     * @param exchange the current server exchange
     * @param chain provides a way to delegate to the next filter
     * @return {@code Mono<Void>} to indicate when request processing is complete
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

}

網關過濾器接口,有且只有一個方法filter,執行當前過濾器,並在此方法中決定過濾器鏈表是否繼續往下執行。

1️⃣、OrderedGatewayFilter--排序

/**
 * 排序的網關路由過濾器,用於包裝真實的網關過濾器,已達到過濾器可排序
 */
public class OrderedGatewayFilter implements GatewayFilter, Ordered {

    //目標過濾器
    private final GatewayFilter delegate;
    //排序字段
    private final int order;

    public OrderedGatewayFilter(GatewayFilter delegate, int order) {
        this.delegate = delegate;
        this.order = order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        return this.delegate.filter(exchange, chain);
    }
}

OrderedGatewayFilter實現類主要目的是爲了將目標過濾器包裝成可排序的對象類型。是目標過濾器的包裝類

2️⃣、GatewayFilterAdapter

  /**
     * 全局過濾器的包裝類,將全局路由包裝成統一的網關過濾器
     */
    private static class GatewayFilterAdapter implements GatewayFilter {

        /**
         * 全局過濾器
         */
        private final GlobalFilter delegate;

        public GatewayFilterAdapter(GlobalFilter delegate) {
            this.delegate = delegate;
        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return this.delegate.filter(exchange, chain);
        }
    }

GatewayFilterAdapter實現類主要目的是爲了將GlobalFilter過濾器包裝成GatewayFilter類型的對應。是GlobalFilter過濾器的包裝類

4.三、GlobalFilter

  

  GlobalFilter 爲請求業務以及路由的URI轉換爲真實業務服務的請求地址的核心過濾器,不須要配置,模式系統初始化時加載,並做用在每一個路由上

1️⃣、初始化加載,經過GatewayAutoConfiguration自動建立

//GatewayAutoConfiguration 類
        /**
         * 全局過濾器,用戶經過HttpClient轉發請求
         */
        @Bean
        public NettyRoutingFilter routingFilter(HttpClient httpClient,
                                                ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
            return new NettyRoutingFilter(httpClient, headersFilters);
        }

        /**
         * 全局的過濾器,用戶將HttpClient客戶端轉發請求的響應寫入到原始的請求響應中
         */
        @Bean
        public NettyWriteResponseFilter nettyWriteResponseFilter(GatewayProperties properties) {
            return new NettyWriteResponseFilter(properties.getStreamingMediaTypes());
        }
        
//GatewayLoadBalancerClientAutoConfiguration 類
    /**
     * 全局過濾器,用於在經過負載均衡客戶端選擇服務實例信息
     */
    @Bean
    @ConditionalOnBean(LoadBalancerClient.class)
    public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client) {
        return new LoadBalancerClientFilter(client);
    }

2️⃣、GlobalFilter轉換成GatewayFilter,並做用於每一個路由上,在FilteringWebHandler實現

//FilteringWebHandler類
    /**
     * 包裝加載全局的過濾器,將全局過濾器包裝成GatewayFilter
     */
    private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
        return filters.stream()
                .map(filter -> {
                    //將全部的全局過濾器包裝成網關過濾器
                    GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
                    //判斷全局過濾器是否實現了可排序接口
                    if (filter instanceof Ordered) {
                        int order = ((Ordered) filter).getOrder();
                        //包裝成可排序的網關過濾器
                        return new OrderedGatewayFilter(gatewayFilter, order);
                    }
                    return gatewayFilter;
                }).collect(Collectors.toList());
    }
    @Override
    public Mono<Void> handle(ServerWebExchange exchange) {
        //獲取請求上下文設置的路由實例
        Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
        //獲取路由定義下的網關過濾器集合
        List<GatewayFilter> gatewayFilters = route.getFilters();

        //組合全局的過濾器與路由配置的過濾器
        List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
        //添加路由配置過濾器到集合尾部
        combined.addAll(gatewayFilters);
        //對過濾器進行排序
        //TODO: needed or cached?
        AnnotationAwareOrderComparator.sort(combined);

        logger.debug("Sorted gatewayFilterFactories: "+ combined);
        //建立過濾器鏈表對其進行鏈式調用
        return new DefaultGatewayFilterChain(combined).filter(exchange);
    }
  • loadFilters方法是將全局路由使用GatewayFilterAdapter包裝成GatewayFilter
  • handle方法
    • 獲取當前請求使用的路由Route
    • 獲取路由配置的過濾器集合route.getFilters()
    • 合併全過濾器與路由配置過濾器combined
    • 對過濾器排序AnnotationAwareOrderComparator.sort
    • 經過過濾器集合構建頂級鏈表DefaultGatewayFilterChain,並對其當前請求調用鏈表的filter方法。
小結

Spring-Cloud-Gateway的過濾器接口分爲兩種:

  1. GlobalFilter : 全局過濾器,不須要在配置文件中配置,做用在全部的路由上,最終經過GatewayFilterAdapter包裝成GatewayFilterChain可識別的過濾器
  2. GatewayFilter : 須要經過spring.cloud.routes.filters 配置在具體路由下,只做用在當前路由上或經過spring.cloud.default-filters配置在全局,做用在全部路由上

5、GatewayFilterFactory 配置路由過濾器

  路由過濾器容許以某種方式修改傳入的HTTP請求或傳出的HTTP響應。路徑過濾器的範圍限定爲特定路徑。 

1️⃣、加載GatewayFilter

  在路由定位器中以及看到了經過路由定義轉換路由方法,其中包含了經過過濾器定義(FilterDefinition)轉換過濾器(GatewayFilter)的部分,在RouteDefinitionRouteLocator類中源碼以下:

/**
     * 加載過濾器,根據過濾器的定義加載
     */
    @SuppressWarnings("unchecked")
    private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
        //遍歷過濾器定義,將過濾器定義轉換成對應的過濾器
        List<GatewayFilter> filters = filterDefinitions.stream()
                .map(definition -> {
                    //經過過濾器定義名稱獲取過濾器建立工廠
                    GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());
                    if (factory == null) {
                        throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
                    }
                    //獲取參數
                    Map<String, String> args = definition.getArgs();
                    if (logger.isDebugEnabled()) {
                        logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
                    }

                    //根據args組裝配置信息
                    Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
                    //構建過濾器建立配置信息
                    Object configuration = factory.newConfig();
                    ConfigurationUtils.bind(configuration, properties,
                            factory.shortcutFieldPrefix(), definition.getName(), validator);

                    //經過過濾器工廠建立GatewayFilter
                    GatewayFilter gatewayFilter = factory.apply(configuration);
                    if (this.publisher != null) {
                        //發佈事件
                        this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));
                    }
                    return gatewayFilter;
                })
                .collect(Collectors.toList());

        ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());
        //包裝過濾器使其全部過濾器繼承Ordered屬性,可進行排序
        for (int i = 0; i < filters.size(); i++) {
            GatewayFilter gatewayFilter = filters.get(i);
            if (gatewayFilter instanceof Ordered) {
                ordered.add(gatewayFilter);
            }
            else {
                ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
            }
        }

        return ordered;
    }

    /**
     * 獲取RouteDefinition中的過濾器集合
     */
    private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
        List<GatewayFilter> filters = new ArrayList<>();

        //校驗gatewayProperties是否含義默認的過濾器集合
        //TODO: support option to apply defaults after route specific filters?
        if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
            //加載全局配置的默認過濾器集合
            filters.addAll(loadGatewayFilters("defaultFilters",
                    this.gatewayProperties.getDefaultFilters()));
        }

        if (!routeDefinition.getFilters().isEmpty()) {
            //加載路由定義中的過濾器集合
            filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
        }

        //排序
        AnnotationAwareOrderComparator.sort(filters);
        return filters;
    }
  • getFilters方法 合併配置中的全局過濾器與路由自身配置的過濾器,並對其排序(全局配置過濾器信息經過gatewayProperties.getDefaultFilters()獲取)
  • loadGatewayFilters 依次遍歷路由定義下的FilterDefinition並將其經過對應的GatewayFilterFactory轉換爲GatewayFilter對象。
2️⃣、GatewayFilterFactory配置過濾器建立工廠建立GatewayFilter對象
  默認內置不少GatewayFilterFactory實現類,用於建立做用不一樣的網關過濾器。
類圖
  

子類及其劃分

3️⃣、AddResponseHeaderGatewayFilterFactory 建立解析

/**
 *
 * 響應header添加數據過濾器
 * 用戶在response header中添加配置數據
 */
public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            //獲取Response並將配置的數據添加到header中
            exchange.getResponse().getHeaders().add(config.getName(), config.getValue());

            return chain.filter(exchange);
        };
    }
}。

配置示例:

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar
  • AddResponseHeader=X-Response-Default-Foo, Default-Bar 會被解析成FilterDefinition對象 (name =AddResponseHeader ,args= [X-Response-Default-Foo,Default-Bar])
  • 通FilterDefinition的Name找到AddResponseHeaderGatewayFilterFactory工廠
  • 經過FilterDefinition 的args 建立Config對象(name=X-Response-Default-Foo,value=Default-Bar)
  • 經過 AddResponseHeaderGatewayFilterFactory工廠的apply方法傳入config建立GatewayFilter對象。

4️⃣、所有配置

5.一、請求頭

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route uri: http://example.org  filters: - AddRequestHeader=X-Request-Foo, Bar

名稱和值,這將爲全部匹配請求的下游請求標頭添加X-Request-Foo:Bar標頭。

移除請求頭

        filters:
        - RemoveRequestHeader=X-Request-Foo

5.二、請求參數

        filters:
        - AddRequestParameter=foo, bar

這會將foo = bar添加到下游請求的全部匹配請求的查詢字符串中。

5.三、添加響應頭

        filters:
        - AddResponseHeader=X-Response-Foo, Bar

這會將X-Response-Foo:Bar標頭添加到全部匹配請求的下游響應標頭中。

移除響應頭

        filters:
        - RemoveResponseHeader=X-Response-Foo

設置響應頭

        filters:
        - SetResponseHeader=X-Response-Foo, Bar

此GatewayFilter將替換具備給定名稱的全部標頭,而不是添加。

5.四、路徑前綴

        filters:
        - PrefixPath=/mypath

這將使/ mypath前綴爲全部匹配請求的路徑。因此對/ hello的請求會被髮送到/ mypath / hello。

5.五、原始主機頭

沒有參數,此過濾器設置路由過濾器將檢查的請求屬性,以肯定是否應發送原始主機頭,而不是http客戶端肯定的主機頭。

        filters:
        - PreserveHostHeader

5.六、重定向

        filters:
        - RedirectTo=302, http://acme.org

這將發送帶有Location:http://acme.org標頭的狀態302以執行重定向。

5.七、重寫路徑

        predicates:
        - Path=/foo/**
        filters:
        - RewritePath=/foo/(?<segment>.*), /$\{segment}

對於/ foo / bar的請求路徑,這將在發出下游請求以前將路徑設置爲/ bar。注意因爲YAML規範,$ \替換爲$。

5.八、保存Session

        predicates:
        - Path=/foo/**
        filters:
        - SaveSession

5.九、路徑模板

SetPath GatewayFilter Factory採用路徑模板參數。它提供了一種經過容許模板化路徑段來操做請求路徑的簡單方法。

        predicates:
        - Path=/foo/{segment} filters: - SetPath=/{segment}

對於/ foo / bar的請求路徑,這將在發出下游請求以前將路徑設置爲/ bar。

5.十、設置響應狀態

spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route uri: http://example.org  filters: - SetStatus=BAD_REQUEST - id: setstatusint_route uri: http://example.org  filters: - SetStatus=401

5.十一、請求參數剝離

parts參數指示在將請求發送到下游以前從請求中剝離的路徑中的部分數。

        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

當經過網關向/ name / bar / foo發出請求時,對nameservice的請求將相似於http:// nameservice / foo。

5.十二、重試

retries:重試:應該嘗試的重試次數

statuses:狀態:應該重試的HTTP狀態代碼,使用org.springframework.http.HttpStatus表示

methods:方法:應該重試的HTTP方法,使用org.springframework.http.HttpMethod表示

series:系列:要重試的狀態代碼系列,使用org.springframework.http.HttpStatus.Series表示

      routes:
      - id: retry_test uri: http://localhost:8080/flakey  predicates: - Host=*.retry.com filters: - name: Retry args: retries: 3 statuses: BAD_GATEWAY

5.1三、Hystrix GatewayFilter Factory

https://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#_hystrix_gatewayfilter_factory

5.1四、請求限速 

  RequestRateLimiter GatewayFilter Factory

5.1五、安全頭

  SecureHeaders GatewayFilter Factory

相關文章
相關標籤/搜索