Spring Cloud Alibaba - 25 Gateway-路由断言工厂Route Predicate Factories谓词工厂示例及源码解析

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
简介: Spring Cloud Alibaba - 25 Gateway-路由断言工厂Route Predicate Factories谓词工厂示例及源码解析

6735aa4777de402592fbe82e8b40ee3d.png

官网


https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories


efda0395c2c841c290c57e6b7fe8c02c.png


Spring Cloud Gateway 将路由匹配为 Spring WebFluxHandlerMapping基础架构的一部分。Spring Cloud Gateway 包含许多内置的路由谓词工厂。所有这些谓词都匹配 HTTP 请求的不同属性。我们可以将多个路由谓词工厂与逻辑and语句结合起来。


The After Route Predicate Factory


https://cloud.spring.io/spring-cloud-gateway/reference/html/#the-after-route-predicate-factory


bc324e779555424785f12365cfaa960b.png


小栗子


我们还是继续老工程 ,启动

artisan-cloud-gateway 【8888】

artisan-cloud-gateway-order

artisan-cloud-gateway-product


网关配置

application-after_route.yml

#  网关的After谓词,对应的源码处理AfterRoutePredicateFactory
#作用: 经过网关的所有请求 当前时间>比After阈值  就进行转发
#现在我们是2022年了 currentTime<After阈值,所以网关不会进行转发,而返回404spring:
spring:
  cloud:
    gateway: #gateway
      routes:
        - id: after_route  # id 确保唯一
          uri: lb://artisan-cloud-gateway-order  #  nacos上的 注册地址
          predicates:
            - After=2025-02-13T18:27:28.309+08:00[Asia/Shanghai]


currentTime<After阈值,所以网关不会进行转发 .

激活配置文件


ed80b0cf416f4559850e6eb9d86a2dd9.png

【测试】

86d3854e4a0d49d58941c26d3d8e8fab.png

符合预期。

如果我们改下时间呢?f4ed71bd62d1431d9be69770f38d2be3.png


550cb642210f44af8d7e2ef186675c18.png

AfterRoutePredicateFactory源码

public class AfterRoutePredicateFactory
    extends AbstractRoutePredicateFactory<AfterRoutePredicateFactory.Config> {
  /**
   * DateTime key.
   */
  public static final String DATETIME_KEY = "datetime";
  public AfterRoutePredicateFactory() {
    super(Config.class);
  }
  @Override
  public List<String> shortcutFieldOrder() {
    return Collections.singletonList(DATETIME_KEY);
  }
  @Override
  public Predicate<ServerWebExchange> apply(Config config) {
    return new GatewayPredicate() {
      @Override
      public boolean test(ServerWebExchange serverWebExchange) {
        final ZonedDateTime now = ZonedDateTime.now();
        return now.isAfter(config.getDatetime());
      }
      @Override
      public String toString() {
        return String.format("After: %s", config.getDatetime());
      }
    };
  }
  public static class Config {
    @NotNull
    private ZonedDateTime datetime;
    public ZonedDateTime getDatetime() {
      return datetime;
    }
    public void setDatetime(ZonedDateTime datetime) {
      this.datetime = datetime;
    }
  }
}


核心方法,apply,比较简单。


The Before Route Predicate Factory


https://cloud.spring.io/spring-cloud-gateway/reference/html/#the-before-route-predicate-factory


7c31b93da03041d487716752eea94fcd.png


小栗子

application-before_route.yml

#目的:测试网关的Before谓词,对应的源码处理BeforeRoutePredicateFactory
#作用: 经过网关的所有请求当前时间 比Before=2021-02-13T18:27:28.309+08:00[Asia/Shanghai] 小  就进行转发
#现在2022年了 时间比配置的阈值大,所以我们不会进行转发,而返回404
#2021-02-13T18:27:28.309+08:00[Asia/Shanghai] 这个时间怎么获取的呢? --- System.out.println(ZonedDateTime.now())
spring:
  cloud:
    gateway: #gateway
      routes:
        - id: before_route  # id 确保唯一
          uri: lb://artisan-cloud-gateway-order
          predicates:
            - Before=2023-02-13T18:27:28.309+08:00[Asia/Shanghai]


BeforeRoutePredicateFactory源码

public class BeforeRoutePredicateFactory
    extends AbstractRoutePredicateFactory<BeforeRoutePredicateFactory.Config> {
  /**
   * DateTime key.
   */
  public static final String DATETIME_KEY = "datetime";
  public BeforeRoutePredicateFactory() {
    super(Config.class);
  }
  @Override
  public List<String> shortcutFieldOrder() {
    return Collections.singletonList(DATETIME_KEY);
  }
  @Override
  public Predicate<ServerWebExchange> apply(Config config) {
    return new GatewayPredicate() {
      @Override
      public boolean test(ServerWebExchange serverWebExchange) {
        final ZonedDateTime now = ZonedDateTime.now();
        return now.isBefore(config.getDatetime());
      }
      @Override
      public String toString() {
        return String.format("Before: %s", config.getDatetime());
      }
    };
  }
  public static class Config {
    private ZonedDateTime datetime;
    public ZonedDateTime getDatetime() {
      return datetime;
    }
    public void setDatetime(ZonedDateTime datetime) {
      this.datetime = datetime;
    }
  }
}


The Between Route Predicate Factory

https://cloud.spring.io/spring-cloud-gateway/reference/html/#the-between-route-predicate-factory

8ed70d6a35d0441faacc62b599a8b32f.png


小栗子

application-between-route.yml

# Between谓词  BetweenRoutePredicateFactory
# 就是经过网关请求的当前时间 currentTime 满足
# Between startTime < currentTime < Between EndTime 才进行转发
spring:
  cloud:
    gateway:
      routes:
        - id: between-route #id必须要唯一
          uri: lb://artisan-cloud-gateway-order  # 这里可以使用负载均衡的写法
          predicates:
            - Between=2020-02-13T18:27:28.309+08:00[Asia/Shanghai],2025-02-13T18:27:28.309+08:00[Asia/Shanghai]


BetweenRoutePredicateFactory源码

public class BetweenRoutePredicateFactory
    extends AbstractRoutePredicateFactory<BetweenRoutePredicateFactory.Config> {
  /**
   * DateTime 1 key.
   */
  public static final String DATETIME1_KEY = "datetime1";
  /**
   * DateTime 2 key.
   */
  public static final String DATETIME2_KEY = "datetime2";
  public BetweenRoutePredicateFactory() {
    super(Config.class);
  }
  @Override
  public List<String> shortcutFieldOrder() {
    return Arrays.asList(DATETIME1_KEY, DATETIME2_KEY);
  }
  @Override
  public Predicate<ServerWebExchange> apply(Config config) {
    Assert.isTrue(config.getDatetime1().isBefore(config.getDatetime2()),
        config.getDatetime1() + " must be before " + config.getDatetime2());
    return new GatewayPredicate() {
      @Override
      public boolean test(ServerWebExchange serverWebExchange) {
        final ZonedDateTime now = ZonedDateTime.now();
        return now.isAfter(config.getDatetime1())
            && now.isBefore(config.getDatetime2());
      }
      @Override
      public String toString() {
        return String.format("Between: %s and %s", config.getDatetime1(),
            config.getDatetime2());
      }
    };
  }
  @Validated
  public static class Config {
    @NotNull
    private ZonedDateTime datetime1;
    @NotNull
    private ZonedDateTime datetime2;
    public ZonedDateTime getDatetime1() {
      return datetime1;
    }
    public Config setDatetime1(ZonedDateTime datetime1) {
      this.datetime1 = datetime1;
      return this;
    }
    public ZonedDateTime getDatetime2() {
      return datetime2;
    }
    public Config setDatetime2(ZonedDateTime datetime2) {
      this.datetime2 = datetime2;
      return this;
    }
  }
}


The Cookie Route Predicate Factory

https://cloud.spring.io/spring-cloud-gateway/reference/html/#the-cookie-route-predicate-factory


1a9555dc649d42f6aaec0595fdf94de0.png


小栗子

application-cookie-route.yml

#谓词 Cookie 源码  CookieRoutePredicateFactory
#表示通过网关的请求 必须带入包含了Cookie name=Company value=Artisan
#才转发请求
spring:
  cloud:
    gateway:
      routes:
        - id: cookie-route #id必须要唯一
          uri: lb://artisan-cloud-gateway-order  # 这里可以使用负载均衡的写法
          predicates:
            #当我们的请求中包含了Cookie name=Company value=Artisan
            #才转发请求
            - Cookie=Company,Artisan


CookieRoutePredicateFactory源码

核心方法

@Override
  public Predicate<ServerWebExchange> apply(Config config) {
    return new GatewayPredicate() {
      @Override
      public boolean test(ServerWebExchange exchange) {
        List<HttpCookie> cookies = exchange.getRequest().getCookies()
            .get(config.name);
        if (cookies == null) {
          return false;
        }
        for (HttpCookie cookie : cookies) {
          if (cookie.getValue().matches(config.regexp)) {
            return true;
          }
        }
        return false;
      }
      @Override
      public String toString() {
        return String.format("Cookie: name=%s regexp=%s", config.name,
            config.regexp);
      }
    };
  }



The Header Route Predicate Factory

https://cloud.spring.io/spring-cloud-gateway/reference/html/#the-header-route-predicate-factory0ad5333f223d4a22a0fb84ed5d5ef0fc.png


小栗子

application-header-route.yml

#Header谓词   源码HeaderRoutePredicateFactory
#说明请求经过网关 必须带入
#header的k=X-Request-appId v=Artisan才会被转发
spring:
  cloud:
    gateway:
      routes:
        - id: header-route #id必须要唯一
          uri: lb://artisan-cloud-gateway-order
          predicates:
            - Header=X-Request-appId,Artisan

HeaderRoutePredicateFactory源码

@Override
  public Predicate<ServerWebExchange> apply(Config config) {
    boolean hasRegex = !StringUtils.isEmpty(config.regexp);
    return new GatewayPredicate() {
      @Override
      public boolean test(ServerWebExchange exchange) {
        List<String> values = exchange.getRequest().getHeaders()
            .getOrDefault(config.header, Collections.emptyList());
        if (values.isEmpty()) {
          return false;
        }
        // values is now guaranteed to not be empty
        if (hasRegex) {
          // check if a header value matches
          return values.stream()
              .anyMatch(value -> value.matches(config.regexp));
        }
        // there is a value and since regexp is empty, we only check existence.
        return true;
      }
      @Override
      public String toString() {
        return String.format("Header: %s regexp=%s", config.header,
            config.regexp);
      }
    };
  }


The Host Route Predicate Factory


678c802c65564cefa96df513a115754b.png


#Host谓词  源码HostRoutePredicateFactory 
#说明请求http://localhost:8888/selectOrderInfoById/1的
#Host必须满足www.artisan.com:8888或者localhost:8888才会
#转发到http://artisan-cloud-gateway-order/selectOrderInfoById/1
#而127.0.0.1不会被转发
spring:
  cloud:
    gateway:
      routes:
        - id: host-route #id必须要唯一
          uri: lb://artisan-cloud-gateway-order
          predicates:
            - Host=www.artisan.com:8888,localhost:8888

The Method Route Predicate Factory


a612ecb296e848bcb081271a2e5dae64.png

#Http请求方法的谓词 Method 源码 MethodRoutePredicateFactory 
#表示经过网关的请求 只有post方式才能被转发
spring:
  cloud:
    gateway:
      routes:
       - id: method #id必须要唯一
         uri: lb://artisan-cloud-gateway-order
         predicates:
           #当前请求的方式 http://localhost:8888/selectOrderInfoById/1 是Post才会被转发
           #到http://artisan-cloud-gateway-order/selectOrderInfoById/1
           - Method=Post


The Path Route Predicate Factory


a03fe6b135eb44edb8b26610fecc7b79.png


850a282ecf414f619917dd85f063774c.png

The Query Route Predicate Factory

d565ec660b684e15bc0a66f82cab9719.png

3f7c72b678784e93a0c3a676d79c97d7.png


The RemoteAddr Route Predicate Factory


a51ee8e25a414238bf8a3dd99a655fea.png

The Weight Route Predicate Factory


755ac97c8a2a43dab5c0a11563f57af1.png


源码


https://github.com/yangshangwei/SpringCloudAlibabMaster

相关文章
|
10天前
|
JavaScript 安全 Java
【绝密攻略】揭秘Spring Boot与Ant Design Pro Vue的终极结合:打造梦幻般的动态路由与菜单管理,颠覆你的前后端分离世界!
【8月更文挑战第9天】随着前后端分离趋势的发展,构建高效且易维护的框架至关重要。本文介绍如何利用Spring Boot与Ant Design Pro Vue打造带有动态路由和菜单的应用。首先需安装Node.js、NPM及Java开发工具;接着通过Spring Initializr初始化含Web和Security依赖的项目,并配置Spring Security。后端API提供菜单数据,而前端则基于这些数据动态生成路由和菜单。通过具体步骤演示整个流程,包括创建Controller、配置动态路由、设置菜单等。此外还分享了实践心得,强调版本兼容性、安全性等方面的重要性。
26 1
|
22天前
|
负载均衡 Java Spring
Spring cloud gateway 如何在路由时进行负载均衡
Spring cloud gateway 如何在路由时进行负载均衡
140 15
|
22天前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
59 11
|
21天前
|
Java Spring
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
40 3
|
21天前
|
Java 微服务 Spring
SpringCloud gateway自定义请求的 httpClient
SpringCloud gateway自定义请求的 httpClient
42 3
|
27天前
|
JSON 前端开发 Java
SpringCloud怎么搭建GateWay网关&统一登录模块
本文来分享一下,最近我在自己的项目中实现的认证服务,目前比较简单,就是可以提供一个公共的服务,专门来处理登录请求,然后我还在API网关处实现了登录拦截的效果,因为在一个博客系统中,有一些地址是可以不登录的,比方说首页;也有一些是必须登录的,比如发布文章、评论等。所以,在网关处可以支持自定义一些不需要登录的地址,一些需要登录的地址,也可以在网关处进行校验,如果未登录,可以返回JSON格式的出参,前端可以进行相关处理,比如跳转到登录页面等。
|
26天前
|
缓存 监控 Java
通用快照方案问题之Spring Boot Admin的定义如何解决
通用快照方案问题之Spring Boot Admin的定义如何解决
34 0
|
21天前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
1月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
87 0
|
18天前
|
XML Java 数据库连接
Spring Boot集成MyBatis
主要系统的讲解了 Spring Boot 集成 MyBatis 的过程,分为基于 xml 形式和基于注解的形式来讲解,通过实际配置手把手讲解了 Spring Boot 中 MyBatis 的使用方式,并针对注解方式,讲解了常见的问题已经解决方式,有很强的实战意义。在实际项目中,建议根据实际情况来确定使用哪种方式,一般 xml 和注解都在用。