008-spring cloud gateway-路由謂詞RoutePredicate、RoutePredicateFactory


一、概述

  Spring Cloud Gateway將路由作為Spring WebFlux HandlerMapping基礎結構的一部分進行匹配。 Spring Cloud Gateway包含許多內置的Route Predicate Factories。所有這些謂詞都匹配HTTP請求的不同屬性。多路線謂詞工廠可以組合,並通過邏輯and。

  路由選擇是通過Predicate函數式接口進行判斷當前路由是否滿足給定條件。

  附注:關於jdk8的Predicate
  Predicate<T> 接受一個輸入參數,返回一個布爾值結果。該接口包含多種默認方法來將Predicate組合成其他復雜的邏輯(比如:與,或,非)。可以用於接口請求參數校驗、判斷新老數據是否有變化需要進行更新操作。
add--與、or--或、negate--非   boolean test(T t); 判斷   Predicate<T> and(Predicate<? super T> other) 接收一個Predicate類型,也就是將傳入的條件和當前條件以並且(AND)的關系組合   Predicate<T> or(Predicate<? super T> other)接收一個Predicate類型,也就是將傳入的條件和當前條件以或(OR)的關系組合   更多參看:https://www.cnblogs.com/bjlhx/p/9711292.html

1.1、路由謂詞創建及使用

1️⃣、加載路由中的Predicate

  在路由定位器中以及看到了通過路由定義轉換路由方法,其中包含了通過謂語定義(PredicateDefinition)轉換謂語(Predicate)的部分,在RouteDefinitionRouteLocator類中源碼如下:
/**
     * 返回組合的謂詞
     * @param routeDefinition
     * @return
     */
    private Predicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
        //獲取RouteDefinition中的PredicateDefinition集合
        List<PredicateDefinition> predicates = routeDefinition.getPredicates();

        Predicate<ServerWebExchange> predicate = lookup(routeDefinition, predicates.get(0));

        for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
            Predicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
            //返回一個組合的謂詞,表示該謂詞與另一個謂詞的短路邏輯AND
            predicate = predicate.and(found);
        }

        return predicate;
    }
    
        /**
     * 獲取一個謂語定義(PredicateDefinition)轉換的謂語
     * @param route
     * @param predicate
     * @return
     */
    @SuppressWarnings("unchecked")
    private Predicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
        //獲取謂語創建工廠
        RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
        if (factory == null) {
            throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
        }
        //獲取參數
        Map<String, String> args = predicate.getArgs();
        if (logger.isDebugEnabled()) {
            logger.debug("RouteDefinition " + route.getId() + " applying "
                    + args + " to " + predicate.getName());
        }

        //組裝參數
        Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
        //構建創建謂語的配置信息
        Object config = factory.newConfig();
        ConfigurationUtils.bind(config, properties,
                factory.shortcutFieldPrefix(), predicate.getName(), validator);
        if (this.publisher != null) {
            this.publisher.publishEvent(new PredicateArgsEvent(this, route.getId(), properties));
        }
        //通過謂語工廠構建謂語
        return factory.apply(config);
    }
  • 獲取路由定義(routeDefinition)所有的謂語定位(PredicateDefinition)
  • 以此根據謂語定義(PredicateDefinition)查找謂語對於的創建工廠(RoutePredicateFactory) 創建謂語
  • 通過 Predicate<T>接口 and方法合並謂語集合返回一個新的復合謂語

2️⃣、使用路由Predicate判斷路由是否可用

  在Spring-Cloud-Gateway之請求處理流程里,在handlerMapping中通過路由定位器獲取所有路由,並過濾掉謂語判斷失敗的路由,最終獲取滿足條件的路由
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
        //通過路由定位器獲取路由信息
        return this.routeLocator.getRoutes()
                .filter(route -> {
            exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, route.getId());
                    //返回通過謂語過濾的路由信息
                    return route.getPredicate().test(exchange);
                });
    }

  通過上面兩步可以看到了整個路由的條件創建以及使用的地方以及流程,Spring-Cloud-Gateway通過Predicate接口完成簡單條件組合以及判斷。

1.2、RoutePredicateFactory 路由謂詞工廠

  Spring-Cloud-Gateway通過RoutePredicateFactory創建Predicate。其中預制了很多RoutePredicateFactory使其可以通過簡單的配置就可以創建出理想的Predicate。
查看類Uml圖
  

按照子類功能划分:

  

1️⃣、MethodRoutePredicateFactory

/**
 * 請求方式(GET,POST,DEL,PUT)校驗匹配創建工廠
 */
public class MethodRoutePredicateFactory extends AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config> {

    public static final String METHOD_KEY = "method";
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            //獲取當前請求的HttpMethod
            HttpMethod requestMethod = exchange.getRequest().getMethod();
            //校驗請求HttpMethod與配置是否一致
            return requestMethod == config.getMethod();
        };
    }

    public static class Config {
        /**
         * http 請求Method
         */
        private HttpMethod method;

        public HttpMethod getMethod() {
            return method;
        }

        public void setMethod(HttpMethod method) {
            this.method = method;
        }
    }
}

配置示例

spring:
  cloud:
    gateway:
      routes:
      # =====================================
      - id: method_route
        uri: http://example.org
        predicates:
        - Method=GET

過程:

  • Method=GET 會被解析成PredicateDefinition對象 (name =Method ,args= GET)
  • 通過PredicateDefinition的Name找到MethodRoutePredicateFactory工廠
  • 通過 PredicateDefinition 的args 創建Config對象(HttpMethod=GET)
  • 通過 MethodRoutePredicateFactory工廠的apply方法傳入config創建Predicate對象。

1.3、全部謂詞配置

1.3.1、After Route Predicate Factory

  此謂詞匹配當前日期時間之后發生的請求。

  application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: after_route uri: http://example.org  predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver]

1.3.2、Before Route Predicate Factory

  此謂詞匹配在當前日期時間之前發生的請求。

  application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: before_route uri: http://example.org  predicates: - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

1.3.3、Between Route Predicate Factory

  此謂詞匹配datetime1之后和datetime2之前發生的請求。 datetime2參數必須在datetime1之后。

  application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: between_route uri: http://example.org  predicates: - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

1.3.4、Cookie Route Predicate Factory

  Cookie Route Predicate Factory有兩個參數,cookie名稱和正則表達式。此謂詞匹配具有給定名稱且值與正則表達式匹配的cookie。

  application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route uri: http://example.org  predicates: - Cookie=chocolate, ch.p

  此路由匹配請求有一個名為chocolate的cookie,其值與ch.p正則表達式匹配。

1.3.5、Header Route Predicate Factory

  Header Route Predicate Factory有兩個參數,標題名稱和正則表達式。此謂詞與具有給定名稱且值與正則表達式匹配的標頭匹配。

  application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: header_route uri: http://example.org  predicates: - Header=X-Request-Id, \d+

  如果請求具有名為X-Request-Id的標頭,則該路由匹配,其值與\ d +正則表達式匹配(具有一個或多個數字的值)。

1.3.6、Host Route Predicate Factory

  Host Route Predicate Factory采用一個參數:主機名模式。該模式是一種Ant樣式模式“.”作為分隔符。此謂詞匹配與模式匹配的Host標頭。

  application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: host_route uri: http://example.org  predicates: - Host=**.somehost.org

  如果請求的主機頭具有值www.somehost.org或beta.somehost.org,則此路由將匹配。

1.3.7、Method Route Predicate Factory

   Method Route Predicate Factory采用一個參數:要匹配的HTTP方法。

spring:
  cloud:
    gateway:
      routes:
      - id: method_route uri: http://example.org  predicates: - Method=GET

1.3.8、Path Route Predicate Factory

spring:
  cloud:
    gateway:
      routes:
      - id: host_route uri: http://example.org  predicates: - Path=/foo/{segment}

/foo/1 or /foo/bar.

1.3.9、Query Route Predicate Factory

Query Route Predicate Factory有兩個參數:一個必需的參數和一個可選的正則表達式。

spring:
  cloud:
    gateway:
      routes:
      - id: query_route uri: http://example.org  predicates: - Query=baz

如果請求包含baz查詢參數,則此路由將匹配。

spring:
  cloud:
    gateway:
      routes:
      - id: query_route uri: http://example.org  predicates: - Query=foo, ba.

如果請求包含其值與ba匹配的foo查詢參數,則此路由將匹配。 regexp,所以bar和baz匹配。

1.3.10、RemoteAddr Route Predicate Factory

RemoteAddr Route Predicate Factory采用CIDR符號(IPv4或IPv6)字符串的列表(最小值為1),例如, 192.168.0.1/16(其中192.168.0.1是IP地址,16是子網掩碼)。

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route uri: http://example.org  predicates: - RemoteAddr=192.168.1.1/24

如果請求的遠程地址是例如192.168.1.10,則此路由將匹配。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM