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,则此路由将匹配。

猜你喜欢

转载自www.cnblogs.com/bjlhx/p/9785926.html