Gateway Predicate断言(谓词)

本文详细介绍了SpringCloudGateway中的内置路由谓词工厂,如AfterRoute、BeforeRoute、BetweenRoute等,以及如何自定义RoutePredicateFactory以实现更复杂的需求。还涵盖了配置和测试这些路由策略的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

是什么

Spring Cloud Gateway匹配路由作为Spring WebFlux HandlerMapping基础设施的一部分。 Spring Cloud Gateway包含许多内置的路由谓词工厂。 所有这些谓词都匹配HTTP请求的不同属性。 您可以使用逻辑 and 语句来联合收割机组合多个路由谓词工厂。

Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。

分类

                                                

idea启动项目打印的日志: 

常用的内置Route Predicate

id:我们自定义的路由 ID,保持唯一
uri:目标服务地址
predicates:路由条件,Predicate接受一个输入参数返回一个布尔值。
        该属性包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)

配置语法总体概述

2种配置方式,二选一

短格式配置

完全展开配置

测试地址

http://localhost:9527/pay/gateway/get/1

After Route Predicate

After路由谓词工厂接受一个datetime参数(这是一个java ZonedDateTime)。

在指定日期时间之后才可以请求该地址。

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - After=2024-04-24T23:56:23.970133500+08:00[Asia/Shanghai]

如何获取ZonedDateTime

import java.time.ZonedDateTime;

public class Testtt {
    public static void main(String[] args) {
        ZonedDateTime zonedDateTime = ZonedDateTime.now().plusMinutes(1);   // 1分钟之后的时间
        System.out.println(zonedDateTime);
    }
}

Before Route Predicate

Before路由谓词工厂接受一个datetime参数(这是一个java ZonedDateTime)。

在指定日期时间之前才可以请求该地址。

spring:
  cloud:
    gateway:
      routes:
        - id: before_route
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - Before=2024-04-25T00:02:10.783066100+08:00[Asia/Shanghai]

Between Route Predicate

Between路由谓词工厂接受两个参数,datetime1和datetime2(都是Java ZonedDateTime)。用英文逗号分隔。

在datetime1之后和datetime2之前可以请求该地址。

datetime2参数必须在datetime1之后。

spring:
  cloud:
    gateway:
      routes:
        - id: between_route
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - Between=2024-04-24T23:56:23.970133500+08:00[Asia/Shanghai], 2024-04-25T00:02:10.783066100+08:00[Asia/Shanghai]

Cookie Route Predicate

Cookie Route Predicate需要两个参数,一个是 Cookie name ,一个是正则表达式。
路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。

spring:
  cloud:
    gateway:
      routes:
        - id: cookie_route
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - Cookie=chocolate, ch.p

Header Route Predicate

Header路由谓词工厂接受两个参数,header和regexp(这是一个Java正则表达式)。

此谓词与具有给定名称且其值与正则表达式匹配的标头匹配。

spring:
  cloud:
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: header_route
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - Header=X-Request-Id, [0-9]+

Host Route Predicate

Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。

它通过参数中的主机地址作为匹配规则。

spring:
  cloud:
    gateway:
      routes:
        - id: pay_routh1
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - Host=**.somehost.org,**.anotherhost.org

Path Route Predicate

路径相匹配的进行路由

spring:
  cloud:
    gateway:
      routes:
        - id: pay_routh1
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**

Query Route Predica

Query路由谓词工厂有两个参数:一个必需的param和一个可选的regexp(这是一个Java正则表达式)。

请求包含param查询参数。

spring:
  cloud:
    gateway:
      routes:
        - id: pay_routh1
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - Query=green

RemoteAddr route predicate

RemoteAddr路由断言工厂采用sources的列表(最小大小为1),这些列表是CIDR表示法(IPv4或IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是IP地址,16是子网掩码)。

什么是计算机网络中的 CIDR

spring:
  cloud:
    gateway:
      routes:
        - id: pay_routh1
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - RemoteAddr=192.168.31.171/24

Method Route Predicat

Method路由谓词工厂接受一个methods参数,它是一个或多个参数:要匹配的HTTP方法。

配置某个请求地址,只能用Get/Post方法访问,方法限制。

spring:
  cloud:
    gateway:
      routes:
        - id: pay_routh1
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - Method=GET,POST

Weight Route Predicat

Weight路由谓词工厂有两个参数:group和weight(一个int)。计算每组的权重。 

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

该路由将把80%的流量转发到weightthigh.org,20%的流量转发到weightlow.org。

自定义断言

规则:

        要么继承AbstractRoutePredicateFactory抽象类

        要么实现RoutePredicateFactory接口

        开头任意取名,但是必须以RoutePredicateFactory后缀结尾

仿照:AfterRoutePredicateFactory

自定义XXXRoutePredicateFactory

1. 新建类名XXX需要以RoutePredicateFactory结尾,并继承AbstractRoutePredicateFactory类。

2. 重写apply方法

3. 新建apply方法所需要的静态内部类MyRoutePredicateFactory.Config,这个Config类就是我们的路由断言规则,重要

4. 空参构造方法,内部调用super

5. 重载 shortcutFieldOrder 方法

        不重载,配置的时候只能完全展开配置,不能短格式配置。

package com.yq.springcloud.predicate;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;

public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {

    public static final String PARAM_USERTYPE = "userType";

    public MyRoutePredicateFactory() {
        super(Config.class);
    }

    /**
     * 返回有关参数数和快捷方式分析顺序的提示
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList(PARAM_USERTYPE);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return serverWebExchange -> {
            //检查request的参数里面,userType是否为指定的值,符合配置就通过
            String userType = serverWebExchange.getRequest().getQueryParams().getFirst(PARAM_USERTYPE);

            if (userType == null) return false;

            //如果说参数存在,就和config的数据进行比较
            if (userType.equals(config.getUserType())) {
                return true;
            }
            return false;
        };
    }

    @Validated
    @Data
    public static class Config {
        @NotBlank
        private String userType; //钻、金、银等用户等级
    }
}

自定义GatewayAutoConfiguration

仿照:org.springframework.cloud.gateway.config.GatewayAutoConfiguration

将自己自定义的断言加入IOC

package com.yq.springcloud.config;

import com.yq.springcloud.predicate.MyRoutePredicateFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration;
import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledPredicate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class })
@AutoConfigureAfter({ GatewayReactiveLoadBalancerClientAutoConfiguration.class,
        GatewayClassPathWarningAutoConfiguration.class })
public class MyGatewayAutoConfiguration {

    @Bean
    @ConditionalOnEnabledPredicate
    public MyRoutePredicateFactory myRoutePredicateFactory() {
        return new MyRoutePredicateFactory();
    }
}

 配置文件中添加配置断言

spring:
  cloud:
    gateway:
      routes:
        - id: pay_routh1
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/get/**
            - My=diamond

测试

1, 启动的时候,已加载到断言中

2. 只有userType=diamond才可以正常访问。

<think>我们正在讨论Spring Cloud Gateway中的谓词断言)组合逻辑。默认情况下,在配置文件中列出的多个谓词是通过逻辑AND组合的,即请求必须满足所有列出的谓词条件才能匹配该路由。但用户现在想知道如何修改这种默认的AND关系,例如实现OR逻辑或其他组合方式。 然而,需要明确的是:**Spring Cloud Gateway 的路由谓词在设计上只支持 AND 组合,不支持直接在配置文件中声明 OR 或 NOT 等逻辑关系**。这是由框架的当前实现决定的。 但是,我们可以通过一些变通方法来实现类似OR的效果: ### 方法1:定义多个路由(最常用) 将原本需要OR组合的谓词条件拆分成多个独立的路由,每个路由使用一个谓词(或一组AND组合的谓词),并将它们指向相同的目标URI。这样,只要满足其中任意一个路由的条件,请求就会被转发到相同的下游服务。 **示例:实现路径为 `/api/a/**` OR 路径为 `/api/b/**` 的路由** ```yaml spring: cloud: gateway: routes: - id: route_a uri: lb://service-name predicates: - Path=/api/a/** # 第一个条件 - id: route_b uri: lb://service-name predicates: - Path=/api/b/** # 第二个条件 ``` 这样,请求路径匹配 `/api/a/**` 或者 `/api/b/**` 都会被路由到 `lb://service-name`。 ### 方法2:自定义谓词工厂(实现复杂逻辑) 如果需要在一个路由中实现复杂的逻辑(如OR、NOT),可以编写一个**自定义的路由谓词工厂**,在这个工厂中实现你需要的逻辑(例如组合多个条件进行OR运算)。 **步骤**: 1. 创建一个类实现 `RoutePredicateFactory` 接口(通常继承 `AbstractRoutePredicateFactory`)。 2. 在配置类中定义你需要的参数(例如,可以接收多个内部谓词条件)。 3. 在 `apply` 方法中编写组合逻辑(例如,循环检查多个条件,只要有一个满足就返回true)。 4. 将该工厂注册为Spring Bean。 5. 在配置中使用这个自定义的谓词。 **示例自定义OR谓词工厂(简化版)**: ```java @Component public class OrRoutePredicateFactory extends AbstractRoutePredicateFactory<OrRoutePredicateFactory.Config> { public OrRoutePredicateFactory() { super(Config.class); } @Override public Predicate<ServerWebExchange> apply(Config config) { return exchange -> { // 遍历配置中的多个谓词,只要有一个满足就返回true for (Predicate<ServerWebExchange> predicate : config.predicates) { if (predicate.test(exchange)) { return true; } } return false; }; } public static class Config { // 这里可以定义配置属性,例如接收一个谓词列表 private List<Predicate<ServerWebExchange>> predicates; public List<Predicate<ServerWebExchange>> getPredicates() { return predicates; } public void setPredicates(List<Predicate<ServerWebExchange>> predicates) { this.predicates = predicates; } } } ``` 但是,请注意:上述示例是概念性的,实际实现中需要解决如何将配置中的其他谓词条件注入到这个工厂中(这可能涉及到复杂的配置解析)。因此,通常更推荐使用第一种方法(多个路由)来实现OR逻辑。 ### 关于NOT逻辑 同样,Spring Cloud Gateway没有内置的NOT谓词工厂。但可以通过自定义谓词工厂来实现,或者利用已有的谓词进行变通(例如,`After`和`Before`组合可以形成NOT时间区间,但其他属性如路径、头等没有直接取反的方式)。自定义NOT谓词工厂的思路: ```java // 伪代码 public class NotRoutePredicateFactory ... { ... public Predicate<ServerWebExchange> apply(Config config) { return exchange -> !config.targetPredicate.test(exchange); } } ``` ### 总结 * **默认行为**:Spring Cloud Gateway 的多个谓词之间是AND关系,无法通过配置直接修改为OR或其他逻辑。 * **替代方案**: * 对于OR逻辑:拆分成多个路由(推荐)。 * 对于复杂逻辑(包括OR、NOT等):自定义谓词工厂(实现较为复杂,需谨慎使用)。 * 设计考虑:网关路由的设计通常遵循“每个路由由一组AND条件定义”,这样的设计清晰且易于管理。如果在一个路由内引入复杂的逻辑组合,可能会降低配置的可读性和可维护性。 因此,在大多数情况下,建议通过定义多个路由来实现OR的效果,而不是尝试修改默认的AND关系。 §§ 相关问题 §§ 1. 在Spring Cloud Gateway中,如何实现一个路由同时匹配多个路径模式(例如 `/api/a/**` 或 `/api/b/**`)但又不希望拆分成两个路由?有什么最佳实践? 2. 自定义谓词工厂时,如何从配置中解析其他内置谓词(例如在自定义的OR谓词中引用Path谓词)?是否有示例代码? 3. 除了拆分路由和自定义工厂,还有哪些方法可以在Spring Cloud Gateway中实现复杂的路由匹配逻辑(如A/B测试、灰度发布)?</think>在 Spring Cloud Gateway 中,**多个谓词断言)默认采用逻辑 AND 组合**,即请求必须同时满足所有谓词条件才能匹配路由。如果需要修改这种默认的 AND 关系(例如实现 OR 逻辑),需要通过特定方法实现: --- ### 一、默认 AND 逻辑的原理 1. **配置表现**: ```yaml predicates: - Path=/api/user/** # 条件1 - Method=GET # 条件2 - Header=X-Token, .* # 条件3 ``` **请求必须同时满足路径、方法和请求头条件**才能匹配路由[^1]。 2. **底层机制**: - 路由加载时,所有谓词通过 `Predicate.and()` 方法组合成单一逻辑表达式[^4]。 - 源码中的组合逻辑(简化): ```java // RoutePredicateHandlerMapping 类 Predicate<ServerWebExchange> combined = predicates.stream() .reduce(Predicate::and) .orElse(exchange -> true); ``` --- ### 二、实现 OR 逻辑的两种方法 #### 方法1:拆分路由(推荐) 通过定义多个路由实现 OR 效果,每个路由独立匹配条件: ```yaml routes: - id: route_or_1 uri: lb://service predicates: - Path=/api/user/** # 条件A - id: route_or_2 uri: lb://service # 相同目标服务 predicates: - Method=POST # 条件B ``` - **效果**:请求满足 `/api/user/**`(条件A)**或** `POST` 方法(条件B)均会路由到 `lb://service`[^2]。 #### 方法2:自定义谓词工厂(复杂场景) 实现支持 OR 逻辑的自定义谓词工厂: 1. 创建自定义工厂类: ```java @Component public class OrRoutePredicateFactory extends AbstractRoutePredicateFactory<OrRoutePredicateFactory.Config> { @Override public Predicate<ServerWebExchange> apply(Config config) { return exchange -> { // 自定义OR逻辑:任意子谓词满足即返回true return config.predicates.stream() .anyMatch(p -> p.test(exchange)); }; } public static class Config { private List<Predicate<ServerWebExchange>> predicates; // Getter/Setter } } ``` 2. 配置使用: ```yaml predicates: - name: Or args: predicates: - Path=/api/user/** - Method=POST ``` --- ### 三、其他逻辑组合的解决方案 | 逻辑类型 | 实现方案 | |----------|--------------------------------------------------------------------------| | **NOT 逻辑** | 自定义工厂返回 `!predicate.test(exchange)` | | **组合逻辑** | 在自定义工厂中实现复杂表达式(如 `(A && B) || C`) | | **权重路由** | 使用内置 `Weight` 谓词(支持按比例分流)[^3] | --- ### 四、注意事项 1. **性能影响**: - 拆分路由不会显著影响性能,网关会高效匹配路由表。 - 自定义工厂需避免复杂计算(如频繁数据库查询)。 2. **调试建议**: - 启用 `spring.cloud.gateway.metrics.enabled=true` 观察路由匹配统计。 - 使用 `Actuator Gateway Endpoint` (`/actuator/gateway/routes`) 检查路由详情[^3]。 3. **设计原则**: - 优先使用拆分路由实现 OR 逻辑(简单可靠)。 - 仅在复杂业务场景(如动态规则)使用自定义工厂。 > **关键结论**:Spring Cloud Gateway **原生不支持修改默认的 AND 逻辑**,但可通过路由拆分或自定义工厂实现 OR/NOT 等组合,其中拆分路由是推荐方案[^1][^2]。 --- ### 相关问题 1. 在自定义谓词工厂中,如何动态加载外部配置(如数据库规则)实现实时路由调整? 2. 使用 `Weight` 谓词时,如何确保流量分配的均匀性?有哪些监控指标? 3. 网关前有负载均衡器(如 Nginx)时,`RemoteAddr` 谓词为何失效?如何获取真实客户端 IP? 4. 如何通过 Spring Cloud Gateway 实现基于 JWT 声明的动态路由(如不同角色访问不同服务)? [^1]: 内置谓词默认通过 AND 组合,这是路由匹配的核心机制[^1]。 [^2]: 拆分路由是官方推荐实现 OR 逻辑的标准方式[^2]。 [^3]: 监控和调试需依赖 Gateway Actuator 端点[^3]。 [^4]: 路由加载时通过 `Predicate.and()` 组合所有条件[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值