第18课:Gateway路由规则、内置谓词、自定义谓词实战

文章目录

适配版本 :Spring Cloud Gateway 5.0.0、Spring Cloud 2025.1.3(Oakwood)、Spring Boot 4.0.8、Spring Cloud Alibaba 2025.1.0.0、JDK 21

课程定位:网关核心能力实战,从路由配置到内置谓词,从路径重写到自定义谓词工厂,掌握路由规则的全部关键点

一、开篇:路由是网关的"灵魂"

第17课我们完成了Gateway的架构认知:理解了Reactor-Netty异步原理,梳理了请求五阶段流转链路,掌握了2025版双栈架构和配置前缀变更。现在进入Gateway最核心的能力------路由。

路由解决的是一个基本问题:什么样的请求,转发到哪个服务。看起来简单,实则不然。真实的业务场景中,路由规则往往非常复杂:只有携带特定Header的灰度用户才路由到新版本;只有来自内网IP的请求才允许访问管理接口;只在秒杀时间段内才路由到秒杀服务;不同App版本的用户路由到不同后端。

这些问题,都需要通过谓词(Predicate) 来精细控制。Gateway内置了十余种谓词工厂,覆盖路径、方法、Header、Cookie、时间、权重、远程地址等几乎所有HTTP请求属性。当内置谓词无法满足需求时,还可以自定义谓词工厂。

本课将从路由配置的基础讲起,逐一剖析内置谓词的用法,实战路径重写、权重路由和灰度路由,最后手把手教你实现自定义谓词工厂。需要特别注意的是:Gateway 5.0的配置前缀发生了根本性变更,沿用旧前缀会导致路由完全不生效------这是本课第一个必须掌握的知识点。

二、路由配置的两种方式

2.1 2025版配置前缀变更(必读)

在动手配置路由之前,必须确认一件事:配置前缀已经变了。

版本 配置前缀
Gateway 4.x(旧) spring.cloud.gateway.*
Gateway 5.x(新) spring.cloud.gateway.server.webflux.*

源码中GatewayProperties.PREFIX的值已变更为spring.cloud.gateway.server.webflux。如果不迁移前缀,路由配置静默不生效 ------启动不会报错,但访问时返回404,日志中只有No RouteDefinition found。

旧写法(Gateway 5.x不认) :

yaml 复制代码
# ❌ 无效配置
spring:
  cloud:
    gateway:
      routes:
        - id: user_route
          uri: lb://service-user

新写法(Gateway 5.x正确) :

yaml 复制代码
# ✅ 正确配置
spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user_route
              uri: lb://service-user
              predicates:
                - Path=/api/user/**

踩坑提示 :可以临时引入spring-boot-properties-migrator来兼容旧前缀,但建议立即迁移到新前缀。旧前缀将在未来版本中彻底移除。

2.2 快捷配置 vs 完全展开

Gateway提供了两种谓词配置方式:快捷方式 和完全展开方式。

快捷配置通过谓词名称识别,后跟等号,再跟逗号分隔的参数值:

yaml 复制代码
spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user_route
              uri: lb://service-user
              predicates:
                - Path=/api/user/**
                - Method=GET

完全展开的参数 更接近标准YAML,使用name和args键值对:

yaml 复制代码
spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user_route
              uri: lb://service-user
              predicates:
                - name: Path
                  args:
                    patterns: /api/user/**
                - name: Method
                  args:
                    methods: GET

两种方式的选型建议:简单谓词用快捷方式,配置简洁;复杂谓词(如多个Path模式、带正则的参数)用完全展开方式,避免逗号分隔导致的歧义。

三、内置谓词工厂详解

Spring Cloud Gateway内置了十余种路由谓词工厂,所有谓词都与HTTP请求的不同属性匹配,多个谓词之间通过AND逻辑组合------请求必须同时满足所有谓词条件才会被路由。

3.1 Path谓词(最常用)

匹配请求路径模式,是日常开发中使用频率最高的谓词。

yaml 复制代码
predicates:
  - Path=/api/user/**
  - Path=/api/order/**,/api/payment/**

Path谓词支持/**通配符和{segment}路径变量。如果需要更灵活的正则匹配(如匹配任意层级路径),可以自定义AntPathRoutePredicateFactory,使用AntPathMatcher替代默认的PathPatternParser。

3.2 Method谓词

匹配HTTP请求方法:

yaml 复制代码
predicates:
  - Method=GET,POST

3.3 Header谓词

匹配请求头中的参数名和值(支持正则表达式):

yaml 复制代码
predicates:
  - Header=X-Request-Id, \d+
  - Header=Authorization, Bearer.*

典型应用场景 :灰度路由------只有携带X-Gray-Version: v2的请求才路由到新版本服务。

3.4 Query谓词

匹配URL查询参数:

yaml 复制代码
predicates:
  - Query=name, Jack
  - Query=debug

第二个参数为正则表达式,不填写时表示只要存在该参数即匹配。

3.5 Cookie谓词

匹配请求Cookie:

yaml 复制代码
predicates:
  - Cookie=JSESSIONID, [a-z0-9]+

3.6 Host谓词

匹配请求Host头:

yaml 复制代码
predicates:
  - Host=**.example.com

3.7 时间类谓词:After / Before / Between

基于请求时间进行匹配,常用于限时活动场景:

yaml 复制代码
predicates:
  # 2030年1月20日之后才路由
  - After=2030-01-20T17:42:47.789-07:00[America/Denver]
  # 秒杀时间段内才路由
  - Between=2026-11-11T00:00:00+08:00[Asia/Shanghai], 2026-11-11T23:59:59+08:00[Asia/Shanghai]

时间格式为ZonedDateTime,可用System.out.println(ZonedDateTime.now())打印当前时区格式。

3.8 RemoteAddr谓词

匹配客户端IP地址(支持CIDR格式):

yaml 复制代码
predicates:
  - RemoteAddr=192.168.1.1/24

典型应用场景:只有内网IP才能访问管理接口。

3.9 Weight谓词(权重路由)

Weight谓词用于灰度发布,根据权重将流量分配到不同版本的服务实例。同一分组内的所有路由权重之和应为100:

yaml 复制代码
spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user_v1
              uri: lb://service-user-v1
              predicates:
                - Path=/api/user/**
                - Weight=user-group, 95
            - id: user_v2
              uri: lb://service-user-v2
              predicates:
                - Path=/api/user/**
                - Weight=user-group, 5

上述配置将95%的流量路由到v1版本,5%路由到v2版本。灰度验证通过后,逐步调整权重直至v2全量。

四、路径重写与灰度路由实战

4.1 RewritePath路径重写

路径重写用于将外部暴露的URL路径转换为后端服务实际接收的路径。例如前端调用/api/user/1,后端服务实际接收/user/1(去掉/api前缀):

yaml 复制代码
spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user_route
              uri: lb://service-user
              predicates:
                - Path=/api/user/**
              filters:
                - RewritePath=/api/user/(?<segment>.*), /user/${segment}

正则命名捕获组 :(?<segment>.*)捕获/api/user/之后的所有内容,${segment}在替换表达式中引用该值。

4.2 灰度路由完整实战

灰度路由的核心思想是:通过请求特征识别灰度用户,将灰度用户路由到新版本。

方案一:基于Header的灰度路由

yaml 复制代码
spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user_gray
              uri: lb://service-user-v2
              predicates:
                - Path=/api/user/**
                - Header=X-Gray-Version, v2
            - id: user_normal
              uri: lb://service-user-v1
              predicates:
                - Path=/api/user/**
              order: 10    # order越小优先级越高,正常路由作为兜底

关键规则 :灰度路由的order值应小于 正常路由的order值,确保灰度请求优先匹配。未携带灰度Header的请求自动落入正常路由。

方案二:基于权重的灰度路由

如前文3.9节所示,通过Weight谓词按比例分配流量,适合"不区分用户,只按比例灰度"的场景。

方案三:基于Cookie的灰度路由

yaml 复制代码
predicates:
  - Path=/api/user/**
  - Cookie=gray, true

方案四:组合条件灰度路由

实际生产中,灰度规则往往是组合的。例如:内网用户 + 特定App版本才路由到新版本:

yaml 复制代码
predicates:
  - Path=/api/user/**
  - RemoteAddr=192.168.1.0/24
  - Header=X-App-Version, 2\.0\..*

五、自定义谓词工厂实战

当内置谓词无法满足复杂业务需求时(如需要查询数据库、解析JWT、判断用户VIP等级),需要实现自定义谓词工厂。

5.1 核心实现步骤

实现自定义谓词工厂需要四步:

步骤一:继承AbstractRoutePredicateFactory<Config>。

步骤二:定义静态内部类Config ,用于接收YAML中的配置参数。

步骤三:实现apply(Config)方法 ,返回一个Predicate<ServerWebExchange>。

步骤四:覆盖shortcutFieldOrder()方法(可选但推荐),定义快捷配置的字段顺序。

5.2 实战:VIP用户灰度谓词工厂

需求:根据请求头中的用户等级,只有VIP用户才路由到新版本服务。

java 复制代码
package com.example.microservice.gateway.predicate;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

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

@Component
public class VipRoutePredicateFactory
        extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {

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

    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("level");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            String userLevel = exchange.getRequest()
                    .getHeaders().getFirst("X-User-Level");
            if (userLevel == null) {
                return false;
            }
            // 比较用户等级是否达到要求(如 GOLD 匹配 GOLD 和 DIAMOND)
            return compareLevel(userLevel, config.getLevel());
        };
    }

    private boolean compareLevel(String actual, String required) {
        List<String> levels = List.of("NORMAL", "SILVER", "GOLD", "DIAMOND");
        int actualIdx = levels.indexOf(actual.toUpperCase());
        int requiredIdx = levels.indexOf(required.toUpperCase());
        return actualIdx >= requiredIdx && requiredIdx >= 0;
    }

    @Validated
    public static class Config {
        private String level;

        public String getLevel() { return level; }
        public void setLevel(String level) { this.level = level; }
    }
}

关键规范:

  • 类名必须以RoutePredicateFactory结尾,如VipRoutePredicateFactory
  • 谓词名称对应类名前缀:VipRoutePredicateFactory → 配置中使用Vip=
  • shortcutFieldOrder()返回的字段顺序决定了快捷配置中参数的顺序

5.3 配置使用

yaml 复制代码
spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user_vip_gray
              uri: lb://service-user-v2
              predicates:
                - Path=/api/user/**
                - Vip=GOLD

快捷配置 :Vip=GOLD会将GOLD自动映射到Config.level字段。

完全展开配置:

yaml 复制代码
predicates:
  - name: Vip
    args:
      level: GOLD

5.4 自定义谓词不生效的常见原因

问题 原因 解决
谓词完全未匹配 未注册为Spring Bean 添加@Component注解
配置解析失败 类名未以RoutePredicateFactory结尾 遵循命名规范
参数值未注入 未覆盖shortcutFieldOrder() 实现该方法并返回字段列表
匹配逻辑异常 apply()中抛出异常 添加空值检查和异常捕获

六、踩坑指南

坑一:配置前缀未迁移导致路由静默失效

现象 :启动无报错,但所有路由返回404,日志中只有No RouteDefinition found。

原因 :使用了旧的spring.cloud.gateway.routes前缀,而Gateway 5.x要求spring.cloud.gateway.server.webflux.routes。

解决 :迁移到新前缀,或临时引入spring-boot-properties-migrator兼容。这个问题没有任何报错提示,是最隐蔽的坑,必须第一优先级排查。

坑二:Path谓词正则表达式不匹配多级路径

现象 :Path=/(.*)/test-file.js无法匹配/segment1/segment2/test-file.js。

原因 :Gateway 5.x默认使用PathPatternParser,不支持任意层级正则匹配。

解决 :自定义AntPathRoutePredicateFactory,使用AntPathMatcher替代,配置AntPath=/**/test-file.js。

坑三:Weight权重路由不生效

现象:配置了Weight谓词,但流量仍然全部路由到一个版本。

原因 :同一分组内的路由必须同时配置Weight谓词,且权重之和为100。如果只有一个路由配置了Weight,流量会全部走该路由。

解决 :确保同一Weight=groupName, weight分组下所有路由都配置了Weight谓词。

坑四:自定义谓词类名不规范导致配置解析失败

现象 :自定义谓词在YAML中配置后启动报错Unable to find RoutePredicateFactory with name 'Vip'。

原因 :类名未以RoutePredicateFactory结尾,Gateway无法从类名推断谓词名称。

解决 :将类名规范为{谓词名}RoutePredicateFactory的格式。

坑五:灰度路由order值配置错误

现象:灰度请求也被路由到了正常版本。

原因 :灰度路由的order值大于正常路由,导致正常路由先匹配。

解决 :灰度路由的order值应小于 正常路由。order越小优先级越高,默认值为0。

七、课后作业

作业一 :配置三条路由规则:/api/user/**路由到service-user,/api/order/**路由到service-order,/api/product/**路由到service-product。验证通过网关访问三个服务的接口。

作业二 :配置一条灰度路由,携带X-Gray-Version: v2 Header的请求路由到service-user-v2,其他请求路由到service-user-v1。使用curl验证两条路由的匹配结果。

作业三 :配置Weight权重路由,将service-user的95%流量路由到v1实例,5%路由到v2实例。通过多次调用观察流量分布。

作业四(进阶) :实现一个自定义谓词工厂TimeBetweenRoutePredicateFactory,支持配置时间段(如09:00-18:00),只有当前时间在该时间段内的请求才路由。在秒杀场景中使用该谓词。

八、下节预告

第19课将进入Gateway过滤器、全局拦截、请求响应统一处理。内容包括局部过滤器与全局过滤器的区别、执行顺序控制、跨域统一配置、请求参数校验、响应结果统一封装、异常统一拦截和日志全局打印。本课完成了路由规则的深度实战,路由决定了"请求去哪里",第19课的过滤器将决定"请求经过网关时做什么"------鉴权、日志、参数修改、响应增强,这些跨切面关注点都将在过滤器中实现。

🔗《最新版 SpringCloud 2025 从入门到实战》系列课程导航

去订阅

第一部分:微服务前置基础 & 新版环境搭建(第1-5课)

第二部分:注册中心核心(Nacos 最新版)(第6-9课)

第三部分:配置中心核心(Nacos配置中心)(第10-12课)

第四部分:服务通信核心(OpenFeign + LoadBalancer)(第13-16课)

第五部分:网关核心(SpringCloud Gateway 新版)(第17-20课)

第六部分:熔断、限流、降级(Sentinel 新版)(第21-24课)

第七部分:微服务监控、链路追踪、日志体系(第25-28课)

第八部分:微服务高阶特性 & 分布式核心能力(第29-31课)

第九部分:企业级完整项目实战 & 架构复盘(第32-35课)

相关推荐
Wang's Blog1 小时前
Java框架 SpringCloud 快速入门: Ribbon 饥饿加载
java·spring cloud·ribbon
Wang's Blog16 小时前
Java框架 SpringCloud 快速入门: Nacos 环境隔离
java·开发语言·spring cloud
Wang's Blog17 小时前
Java框架 SpringCloud 快速入门: Nacos 服务多级存储模型
java·spring·spring cloud
弈栈录19 小时前
Spring Cloud 微服务架构:注册中心、配置中心与网关
java·spring cloud·架构
Wang's Blog2 天前
Java框架 SpringCloud 快速入门: 实现 Feign 最佳实践(抽取方式)
java·spring cloud
Wang's Blog2 天前
Java框架 SpringCloud 快速入门: Feign 的性能优化
java·spring cloud·性能优化
2601_962177302 天前
2026年AI API Gateway怎么选?我整理了6种方案的费用、稳定性和适用场景
网络·人工智能·深度学习·gateway
Wang's Blog2 天前
Java框架 SpringCloud 快速入门: Feign 的自定义配置与日志级别
java·开发语言·spring cloud
Wang's Blog2 天前
Java框架 SpringCloud 快速入门: Eureka 服务发现与服务名调用改造
java·spring cloud·eureka