文章目录
- 一、开篇:路由是网关的“灵魂”
- 二、路由配置的两种方式
- 2.1 2025版配置前缀变更(必读)
- 2.2 快捷配置 vs 完全展开
- 三、内置谓词工厂详解
- 3.1 Path谓词(最常用)
- 3.2 Method谓词
- 3.3 Header谓词
- 3.4 Query谓词
- 3.5 Cookie谓词
- 3.6 Host谓词
- 3.7 时间类谓词:After / Before / Between
- 3.8 RemoteAddr谓词
- 3.9 Weight谓词(权重路由)
- 四、路径重写与灰度路由实战
- 4.1 RewritePath路径重写
- 4.2 灰度路由完整实战
- 五、自定义谓词工厂实战
- 5.1 核心实现步骤
- 5.2 实战:VIP用户灰度谓词工厂
- 5.3 配置使用
- 5.4 自定义谓词不生效的常见原因
- 六、踩坑指南
- 坑一:配置前缀未迁移导致路由静默失效
- 坑二:Path谓词正则表达式不匹配多级路径
- 坑三:Weight权重路由不生效
- 坑四:自定义谓词类名不规范导致配置解析失败
- 坑五:灰度路由order值配置错误
- 七、课后作业
- 八、下节预告
- 🔗《最新版 SpringCloud 2025 从入门到实战》系列课程导航
适配版本: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不认):
# ❌ 无效配置spring:cloud:gateway:routes:-id:user_routeuri:lb://service-user新写法(Gateway 5.x正确):
# ✅ 正确配置spring:cloud:gateway:server:webflux:routes:-id:user_routeuri:lb://service-userpredicates:-Path=/api/user/**踩坑提示:可以临时引入
spring-boot-properties-migrator来兼容旧前缀,但建议立即迁移到新前缀。旧前缀将在未来版本中彻底移除。
2.2 快捷配置 vs 完全展开
Gateway提供了两种谓词配置方式:快捷方式和完全展开方式。
快捷配置通过谓词名称识别,后跟等号,再跟逗号分隔的参数值:
spring:cloud:gateway:server:webflux:routes:-id:user_routeuri:lb://service-userpredicates:-Path=/api/user/**-Method=GET完全展开的参数更接近标准YAML,使用name和args键值对:
spring:cloud:gateway:server:webflux:routes:-id:user_routeuri:lb://service-userpredicates:-name:Pathargs:patterns:/api/user/**-name:Methodargs:methods:GET两种方式的选型建议:简单谓词用快捷方式,配置简洁;复杂谓词(如多个Path模式、带正则的参数)用完全展开方式,避免逗号分隔导致的歧义。
三、内置谓词工厂详解
Spring Cloud Gateway内置了十余种路由谓词工厂,所有谓词都与HTTP请求的不同属性匹配,多个谓词之间通过AND逻辑组合——请求必须同时满足所有谓词条件才会被路由。
3.1 Path谓词(最常用)
匹配请求路径模式,是日常开发中使用频率最高的谓词。
predicates:-Path=/api/user/**-Path=/api/order/**,/api/payment/**Path谓词支持/**通配符和{segment}路径变量。如果需要更灵活的正则匹配(如匹配任意层级路径),可以自定义AntPathRoutePredicateFactory,使用AntPathMatcher替代默认的PathPatternParser。
3.2 Method谓词
匹配HTTP请求方法:
predicates:-Method=GET,POST3.3 Header谓词
匹配请求头中的参数名和值(支持正则表达式):
predicates:-Header=X-Request-Id,\d+-Header=Authorization,Bearer.*典型应用场景:灰度路由——只有携带X-Gray-Version: v2的请求才路由到新版本服务。
3.4 Query谓词
匹配URL查询参数:
predicates:-Query=name,Jack-Query=debug第二个参数为正则表达式,不填写时表示只要存在该参数即匹配。
3.5 Cookie谓词
匹配请求Cookie:
predicates:-Cookie=JSESSIONID,[a-z0-9]+3.6 Host谓词
匹配请求Host头:
predicates:-Host=**.example.com3.7 时间类谓词:After / Before / Between
基于请求时间进行匹配,常用于限时活动场景:
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格式):
predicates:-RemoteAddr=192.168.1.1/24典型应用场景:只有内网IP才能访问管理接口。
3.9 Weight谓词(权重路由)
Weight谓词用于灰度发布,根据权重将流量分配到不同版本的服务实例。同一分组内的所有路由权重之和应为100:
spring:cloud:gateway:server:webflux:routes:-id:user_v1uri:lb://service-user-v1predicates:-Path=/api/user/**-Weight=user-group,95-id:user_v2uri:lb://service-user-v2predicates:-Path=/api/user/**-Weight=user-group,5上述配置将95%的流量路由到v1版本,5%路由到v2版本。灰度验证通过后,逐步调整权重直至v2全量。
四、路径重写与灰度路由实战
4.1 RewritePath路径重写
路径重写用于将外部暴露的URL路径转换为后端服务实际接收的路径。例如前端调用/api/user/1,后端服务实际接收/user/1(去掉/api前缀):
spring:cloud:gateway:server:webflux:routes:-id:user_routeuri:lb://service-userpredicates:-Path=/api/user/**filters:-RewritePath=/api/user/(?<segment>.*),/user/${segment}正则命名捕获组:(?<segment>.*)捕获/api/user/之后的所有内容,${segment}在替换表达式中引用该值。
4.2 灰度路由完整实战
灰度路由的核心思想是:通过请求特征识别灰度用户,将灰度用户路由到新版本。
方案一:基于Header的灰度路由
spring:cloud:gateway:server:webflux:routes:-id:user_grayuri:lb://service-user-v2predicates:-Path=/api/user/**-Header=X-Gray-Version,v2-id:user_normaluri:lb://service-user-v1predicates:-Path=/api/user/**order:10# order越小优先级越高,正常路由作为兜底关键规则:灰度路由的order值应小于正常路由的order值,确保灰度请求优先匹配。未携带灰度Header的请求自动落入正常路由。
方案二:基于权重的灰度路由
如前文3.9节所示,通过Weight谓词按比例分配流量,适合“不区分用户,只按比例灰度”的场景。
方案三:基于Cookie的灰度路由
predicates:-Path=/api/user/**-Cookie=gray,true方案四:组合条件灰度路由
实际生产中,灰度规则往往是组合的。例如:内网用户 + 特定App版本才路由到新版本:
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用户才路由到新版本服务。
packagecom.example.microservice.gateway.predicate;importorg.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;importorg.springframework.stereotype.Component;importorg.springframework.web.server.ServerWebExchange;importjava.util.List;importjava.util.function.Predicate;@ComponentpublicclassVipRoutePredicateFactoryextendsAbstractRoutePredicateFactory<VipRoutePredicateFactory.Config>{publicVipRoutePredicateFactory(){super(Config.class);}@OverridepublicList<String>shortcutFieldOrder(){returnList.of("level");}@OverridepublicPredicate<ServerWebExchange>apply(Configconfig){returnexchange->{StringuserLevel=exchange.getRequest().getHeaders().getFirst("X-User-Level");if(userLevel==null){returnfalse;}// 比较用户等级是否达到要求(如 GOLD 匹配 GOLD 和 DIAMOND)returncompareLevel(userLevel,config.getLevel());};}privatebooleancompareLevel(Stringactual,Stringrequired){List<String>levels=List.of("NORMAL","SILVER","GOLD","DIAMOND");intactualIdx=levels.indexOf(actual.toUpperCase());intrequiredIdx=levels.indexOf(required.toUpperCase());returnactualIdx>=requiredIdx&&requiredIdx>=0;}@ValidatedpublicstaticclassConfig{privateStringlevel;publicStringgetLevel(){returnlevel;}publicvoidsetLevel(Stringlevel){this.level=level;}}}关键规范:
- 类名必须以
RoutePredicateFactory结尾,如VipRoutePredicateFactory - 谓词名称对应类名前缀:
VipRoutePredicateFactory→ 配置中使用Vip= shortcutFieldOrder()返回的字段顺序决定了快捷配置中参数的顺序
5.3 配置使用
spring:cloud:gateway:server:webflux:routes:-id:user_vip_grayuri:lb://service-user-v2predicates:-Path=/api/user/**-Vip=GOLD快捷配置:Vip=GOLD会将GOLD自动映射到Config.level字段。
完全展开配置:
predicates:-name:Vipargs:level:GOLD5.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: v2Header的请求路由到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课)