Skip to content

路由谓词工厂

Spring Cloud Gateway 作为微服务架构中的 API 网关,通过路由谓词工厂(Route Predicate Factory)来决定请求应该被路由到哪个服务。谓词工厂就像是网关的"路由规则引擎",它会检查 HTTP 请求的各种属性,然后决定是否匹配特定的路由。

什么是路由谓词工厂?

路由谓词工厂是 Spring Cloud Gateway 中用于匹配 HTTP 请求的组件。它基于 Spring WebFlux 的HandlerMapping基础设施工作,每个谓词工厂都会检查 HTTP 请求的不同属性(如时间、路径、请求头等),并返回匹配结果。多个谓词工厂可以通过逻辑AND操作组合使用。

INFO

  • After、Before、Between:基于时间的谓词工厂
  • Cookie、Header、Host、Method、Path、Query:基于 HTTP 请求属性的谓词工厂
  • RemoteAddr、XForwardedRemoteAddr:基于网络地址的谓词工厂
  • Weight:基于权重的谓词工厂,用于流量分配
  • 组合谓词:可以将多个谓词工厂组合使用,实现复杂的路由逻辑

路由谓词工厂解决了微服务架构中请求路由的核心问题:如何根据请求特征智能地将请求转发到合适的后端服务。

1. 时间相关谓词工厂

After 谓词工厂

After谓词工厂用于匹配指定时间之后的请求,常用于定时发布功能灰度发布场景。

业务场景:新功能需要在特定时间点(如 2024 年 12 月 1 日)才对外开放。

kotlin
@Configuration
class GatewayConfig {

    @Bean
    fun customRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("new_feature_route") { r ->
                r.after(ZonedDateTime.of(2024, 12, 1, 0, 0, 0, 0, ZoneId.of("Asia/Shanghai")))
                 .uri("https://new-feature-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: new_feature_route
          uri: https://new-feature-service.com
          predicates:
            - After=2024-12-01T00:00:00.000+08:00[Asia/Shanghai]

Before 谓词工厂

Before谓词工厂匹配指定时间之前的请求,适用于限时活动系统维护场景。

业务场景:双十一活动只在 11 月 11 日 24 点前有效。

kotlin
@Configuration
class GatewayConfig {

    @Bean
    fun doubleElevenRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("double_eleven_route") { r ->
                r.before(ZonedDateTime.of(2024, 11, 12, 0, 0, 0, 0, ZoneId.of("Asia/Shanghai")))
                 .uri("https://promotion-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: double_eleven_route
          uri: https://promotion-service.com
          predicates:
            - Before=2024-11-12T00:00:00.000+08:00[Asia/Shanghai]

Between 谓词工厂

Between谓词工厂匹配两个时间点之间的请求,完美适用于限时活动维护窗口等场景。

业务场景:系统维护窗口,只有在维护时间段内才路由到维护页面。

kotlin
@Configuration
class GatewayConfig {

    @Bean
    fun maintenanceRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("maintenance_route") { r ->
                r.between(
                    ZonedDateTime.of(2024, 6, 15, 2, 0, 0, 0, ZoneId.of("Asia/Shanghai")),
                    ZonedDateTime.of(2024, 6, 15, 6, 0, 0, 0, ZoneId.of("Asia/Shanghai"))
                )
                 .uri("https://maintenance-page.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: maintenance_route
          uri: https://maintenance-page.com
          predicates:
            - Between=2024-06-15T02:00:00.000+08:00[Asia/Shanghai], 2024-06-15T06:00:00.000+08:00[Asia/Shanghai]

TIP

时间相关的谓词工厂常用于实现定时发布、灰度发布、限时活动等业务场景。使用时注意时区设置,避免因时区问题导致的路由错误。

2. HTTP 请求属性谓词工厂

Cookie谓词工厂根据 Cookie 的名称和值(支持正则表达式)进行匹配,常用于用户身份验证个性化路由

业务场景:VIP 用户通过特定 Cookie 路由到专用服务,获得更好的服务质量。

kotlin
@Component
class VipUserRoute {

    @Bean
    fun vipUserRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("vip_user_route") { r ->
                r.cookie("user_type", "vip.*")  // 匹配vip开头的用户类型
                 .uri("https://vip-service.com")
            }
            .route("normal_user_route") { r ->
                r.cookie("user_type", "normal")
                 .uri("https://normal-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: vip_user_route
          uri: https://vip-service.com
          predicates:
            - Cookie=user_type, vip.*
        - id: normal_user_route
          uri: https://normal-service.com
          predicates:
            - Cookie=user_type, normal

Header 谓词工厂

Header谓词工厂根据 HTTP 请求头进行匹配,常用于API 版本控制设备类型路由等场景。

业务场景:根据客户端类型(移动端、Web 端)路由到不同的服务。

kotlin
@Component
class ClientTypeRoute {

    @Bean
    fun clientTypeRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("mobile_api_route") { r ->
                r.header("X-Client-Type", "mobile")
                 .uri("https://mobile-api.com")
            }
            .route("web_api_route") { r ->
                r.header("X-Client-Type", "web")
                 .uri("https://web-api.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: mobile_api_route
          uri: https://mobile-api.com
          predicates:
            - Header=X-Client-Type, mobile
        - id: web_api_route
          uri: https://web-api.com
          predicates:
            - Header=X-Client-Type, web

API 版本控制示例

kotlin
@Component
class ApiVersionRoute {

    @Bean
    fun apiVersionRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("api_v1_route") { r ->
                r.header("X-API-Version", "v1")
                 .uri("https://api-v1.com")
            }
            .route("api_v2_route") { r ->
                r.header("X-API-Version", "v2")
                 .uri("https://api-v2.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: api_v1_route
          uri: https://api-v1.com
          predicates:
            - Header=X-API-Version, v1
        - id: api_v2_route
          uri: https://api-v2.com
          predicates:
            - Header=X-API-Version, v2

Host 谓词工厂

Host谓词工厂根据 Host 请求头进行匹配,支持 Ant 风格的模式匹配,常用于多租户系统环境隔离

业务场景:多租户 SaaS 系统,不同租户使用不同子域名访问各自的服务实例。

Host 谓词工厂提取的 URI 模板变量(如{tenantId})会被存储在ServerWebExchange.getAttributes()中,可以在后续的过滤器中使用。

kotlin
@Component
class MultiTenantRoute {

    @Bean
    fun tenantRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("tenant_a_route") { r ->
                r.host("*.tenant-a.example.com")
                 .uri("https://tenant-a-service.com")
            }
            .route("tenant_b_route") { r ->
                r.host("*.tenant-b.example.com")
                 .uri("https://tenant-b-service.com")
            }
            .route("tenant_pattern_route") { r ->
                r.host("**.tenant-{tenantId}.example.com")
                 .uri("https://tenant-service.com")
            }
            .route("default_tenant_route") { r ->
                r.host("**.example.com")
                 .uri("https://default-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: tenant_a_route
          uri: https://tenant-a-service.com
          predicates:
            - Host=*.tenant-a.example.com
        - id: tenant_b_route
          uri: https://tenant-b-service.com
          predicates:
            - Host=*.tenant-b.example.com
        - id: tenant_pattern_route
          uri: https://tenant-service.com
          predicates:
            - Host=**.tenant-{tenantId}.example.com
        - id: default_tenant_route
          uri: https://default-service.com
          predicates:
            - Host=**.example.com

Method 谓词工厂

Method谓词工厂根据 HTTP 方法进行匹配,常用于RESTful API 路由读写分离等场景。

读写分离架构,GET 请求路由到只读服务,POST/PUT/DELETE 路由到读写服务。

kotlin
@Component
class ReadWriteSeparationRoute {

    @Bean
    fun readWriteRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("read_only_route") { r ->
                r.method(HttpMethod.GET)
                 .uri("https://read-only-service.com")
            }
            .route("read_write_route") { r ->
                r.method(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE)
                 .uri("https://read-write-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: read_only_route
          uri: https://read-only-service.com
          predicates:
            - Method=GET
        - id: read_write_route
          uri: https://read-write-service.com
          predicates:
            - Method=POST,PUT,DELETE

Path 谓词工厂

Path谓词工厂是最常用的谓词工厂之一,根据请求路径进行匹配,支持 Spring 的PathMatcher模式。

业务场景:微服务架构中根据 API 路径路由到不同的服务。

kotlin
@Component
class MicroserviceRoute {

    @Bean
    fun microserviceRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("user_service_route") { r ->
                r.path("/api/users/**")
                 .uri("https://user-service.com")
            }
            .route("order_service_route") { r ->
                r.path("/api/orders/**")
                 .uri("https://order-service.com")
            }
            .route("product_service_route") { r ->
                r.path("/api/products/**")
                 .uri("https://product-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user_service_route
          uri: https://user-service.com
          predicates:
            - Path=/api/users/**
        - id: order_service_route
          uri: https://order-service.com
          predicates:
            - Path=/api/orders/**
        - id: product_service_route
          uri: https://product-service.com
          predicates:
            - Path=/api/products/**

路径变量提取示例

业务场景:提取路径中的变量

kotlin
@Component
class PathVariableExtraction {

    @Bean
    fun pathVariableRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("user_detail_route") { r ->
                r.path("/api/users/{userId}")
                 .uri("https://user-service.com")
            }
            .route("order_detail_route") { r ->
                r.path("/api/orders/{orderId}/items/{itemId}")
                 .uri("https://order-service.com")
            }
            .build()
    }

    // 在过滤器中获取路径变量
    @Bean
    fun pathVariableFilter(): GatewayFilter {
        return GatewayFilter { exchange, chain ->
            val uriVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange)
            val userId = uriVariables["userId"]
            val orderId = uriVariables["orderId"]

            // 可以将路径变量添加到请求头中传递给下游服务
            if (userId != null) {
                exchange.request.mutate()
                    .header("X-User-Id", userId)
                    .build()
            }

            chain.filter(exchange)
        }
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user_detail_route
          uri: https://user-service.com
          predicates:
            - Path=/api/users/{userId}
        - id: order_detail_route
          uri: https://order-service.com
          predicates:
            - Path=/api/orders/{orderId}/items/{itemId}

WARNING

当设置了spring.webflux.base-path属性时,该值会自动添加到路径模式前缀。例如,spring.webflux.base-path=/app和路径模式/red/{segment},实际匹配的完整模式为/app/red/{segment}

Query 谓词工厂

Query谓词工厂根据查询参数进行匹配,常用于功能开关A/B 测试等场景。

业务场景:A/B 测试,根据查询参数决定使用新版本还是旧版本的服务。

kotlin
@Component
class FeatureFlagRoute {

    @Bean
    fun featureFlagRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("feature_flag_new") { r ->
                r.query("version", "new")
                 .uri("https://new-feature-service.com")
            }
            .route("feature_flag_beta") { r ->
                r.query("beta", "true")
                 .uri("https://beta-service.com")
            }
            .route("default_service") { r ->
                r.query("version")  // 只要存在version参数
                 .uri("https://default-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: feature_flag_new
          uri: https://new-feature-service.com
          predicates:
            - Query=version, new
        - id: feature_flag_beta
          uri: https://beta-service.com
          predicates:
            - Query=beta, true
        - id: default_service
          uri: https://default-service.com
          predicates:
            - Query=version

3. 网络相关谓词工厂

RemoteAddr 谓词工厂

RemoteAddr谓词工厂根据客户端 IP 地址进行匹配,支持 CIDR 表示法,常用于地理位置路由安全控制等场景。

业务场景:内网管理请求路由到管理服务,外网请求路由到公开服务。

kotlin
@Component
class NetworkAccessRoute {

    @Bean
    fun networkAccessRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("internal_admin_route") { r ->
                r.remoteAddr("192.168.1.0/24", "10.0.0.0/8")
                 .uri("https://admin-service.com")
            }
            .route("public_service_route") { r ->
                r.remoteAddr("0.0.0.0/0")
                 .uri("https://public-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: internal_admin_route
          uri: https://admin-service.com
          predicates:
            - RemoteAddr=192.168.1.0/24,10.0.0.0/8
        - id: public_service_route
          uri: https://public-service.com
          predicates:
            - RemoteAddr=0.0.0.0/0

高级配置:处理代理环境

在生产环境中,Gateway 通常部署在负载均衡器或反向代理后面,需要正确解析真实客户端 IP:

kotlin
@Configuration
class RemoteAddrConfig {

    @Bean
    fun remoteAddrRoutes(builder: RouteLocatorBuilder): RouteLocator {
        // 信任一层代理的配置
        val resolver = XForwardedRemoteAddressResolver.maxTrustedIndex(1)

        return builder.routes()
            .route("trusted_proxy_route") { r ->
                r.remoteAddr(resolver, "10.1.1.0/24")
                 .uri("https://internal-service.com")
            }
            .route("direct_access_route") { r ->
                r.remoteAddr("192.168.1.0/24")
                 .uri("https://direct-service.com")
            }
            .build()
    }
}
yaml
# 对于代理环境,需要在代码中配置RemoteAddressResolver
# YAML配置仅适用于直接IP匹配场景
spring:
  cloud:
    gateway:
      routes:
        - id: direct_access_route
          uri: https://direct-service.com
          predicates:
            - RemoteAddr=192.168.1.0/24

XForwarded Remote Addr 谓词工厂

专门处理X-Forwarded-For请求头的谓词工厂,适用于代理环境下的 IP 匹配。

业务场景:CDN 加速场景下,需要根据真实用户 IP 进行地理位置路由。

kotlin
@Component
class XForwardedRemoteAddrRoute {

    @Bean
    fun xForwardedRemoteAddrRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("china_users_route") { r ->
                r.xForwardedRemoteAddr("114.114.114.0/24", "223.5.5.0/24")
                 .uri("https://china-service.com")
            }
            .route("global_users_route") { r ->
                r.xForwardedRemoteAddr("0.0.0.0/0")
                 .uri("https://global-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: china_users_route
          uri: https://china-service.com
          predicates:
            - XForwardedRemoteAddr=114.114.114.0/24,223.5.5.0/24
        - id: global_users_route
          uri: https://global-service.com
          predicates:
            - XForwardedRemoteAddr=0.0.0.0/0

4. 负载均衡相关谓词工厂

Weight 谓词工厂

Weight谓词工厂根据权重进行流量分配,常用于金丝雀发布蓝绿部署灰度发布等场景。

新版本灰度发布,10%流量路由到新版本,90%流量保持在稳定版本。

kotlin
@Component
class CanaryDeploymentRoute {

    @Bean
    fun canaryDeploymentRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("stable_version") { r ->
                r.weight("version_group", 90)
                 .uri("https://stable-service.com")
            }
            .route("canary_version") { r ->
                r.weight("version_group", 10)
                 .uri("https://canary-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: stable_version
          uri: https://stable-service.com
          predicates:
            - Weight=version_group, 90
        - id: canary_version
          uri: https://canary-service.com
          predicates:
            - Weight=version_group, 10

更复杂的灰度发布策略

业务场景:运行时动态调整权重的功能

kotlin
@Configuration
@ConfigurationProperties(prefix = "canary.deployment")
class AdvancedCanaryDeployment {
    var stableWeight: Int = 90
    var canaryWeight: Int = 10
    var betaWeight: Int = 0

    @Bean
    fun canaryRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("stable_service") { r ->
                r.weight("deployment_group", stableWeight)
                 .uri("https://stable-service.com")
            }
            .route("canary_service") { r ->
                r.weight("deployment_group", canaryWeight)
                 .uri("https://canary-service.com")
            }
            .route("beta_service") { r ->
                r.weight("deployment_group", betaWeight)
                 .uri("https://beta-service.com")
            }
            .build()
    }

    // 运行时动态调整权重的功能
    @EventListener
    fun handleWeightChange(event: WeightChangeEvent) {
        // 实现动态权重调整逻辑
        when (event.targetVersion) {
            "canary" -> {
                canaryWeight = event.newWeight
                stableWeight = 100 - canaryWeight - betaWeight
            }
            "beta" -> {
                betaWeight = event.newWeight
                stableWeight = 100 - canaryWeight - betaWeight
            }
        }
    }
}

data class WeightChangeEvent(
    val targetVersion: String,
    val newWeight: Int
)
yaml
canary:
  deployment:
    stable-weight: 90
    canary-weight: 10
    beta-weight: 0

spring:
  cloud:
    gateway:
      routes:
        - id: stable_service
          uri: https://stable-service.com
          predicates:
            - Weight=deployment_group, ${canary.deployment.stable-weight}
        - id: canary_service
          uri: https://canary-service.com
          predicates:
            - Weight=deployment_group, ${canary.deployment.canary-weight}
        - id: beta_service
          uri: https://beta-service.com
          predicates:
            - Weight=deployment_group, ${canary.deployment.beta-weight}

5. 组合谓词的高级用法

业务场景:组合多个谓词来实现复杂的路由逻辑

kotlin
@Configuration
class ComplexRouting {

    @Bean
    fun complexRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            // VIP用户的移动端API v2版本
            .route("vip_mobile_api_v2") { r ->
                r.cookie("user_type", "vip")
                 .and()
                 .header("X-Client-Type", "mobile")
                 .and()
                 .header("X-API-Version", "v2")
                 .and()
                 .path("/api/**")
                 .uri("https://vip-mobile-api-v2.com")
            }
            // 工作时间的管理接口(内网访问)
            .route("admin_working_hours") { r ->
                r.remoteAddr("192.168.1.0/24")
                 .and()
                 .between(
                     ZonedDateTime.now().with(LocalTime.of(9, 0)),
                     ZonedDateTime.now().with(LocalTime.of(18, 0))
                 )
                 .and()
                 .path("/admin/**")
                 .uri("https://admin-service.com")
            }
            // 限时活动的特殊路由
            .route("special_promotion") { r ->
                r.between(
                     ZonedDateTime.of(2024, 11, 11, 0, 0, 0, 0, ZoneId.of("Asia/Shanghai")),
                     ZonedDateTime.of(2024, 11, 12, 0, 0, 0, 0, ZoneId.of("Asia/Shanghai"))
                 )
                 .and()
                 .query("promotion", "double11")
                 .and()
                 .method(HttpMethod.GET, HttpMethod.POST)
                 .uri("https://promotion-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        # VIP用户的移动端API v2版本
        - id: vip_mobile_api_v2
          uri: https://vip-mobile-api-v2.com
          predicates:
            - Cookie=user_type, vip
            - Header=X-Client-Type, mobile
            - Header=X-API-Version, v2
            - Path=/api/**
        # 限时活动的特殊路由
        - id: special_promotion
          uri: https://promotion-service.com
          predicates:
            - Between=2024-11-11T00:00:00.000+08:00[Asia/Shanghai], 2024-11-12T00:00:00.000+08:00[Asia/Shanghai]
            - Query=promotion, double11
            - Method=GET,POST

6. 实际业务场景案例

电商系统的路由策略

配置示例

kotlin
@Configuration
class ECommerceGateway {

    @Bean
    fun ecommerceRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            // VIP用户路由到高性能服务
            .route("vip_user_service") { r ->
                r.cookie("membership", "vip|premium")
                 .and()
                 .path("/api/products/**", "/api/orders/**")
                 .uri("https://vip-service.com")
            }
            // 移动端用户路由到移动优化服务
            .route("mobile_optimized_service") { r ->
                r.header("User-Agent", ".*Mobile.*")
                 .and()
                 .path("/api/**")
                 .uri("https://mobile-service.com")
            }
            // 促销时段的特殊路由
            .route("promotion_service") { r ->
                r.between(
                     ZonedDateTime.now().withHour(10),
                     ZonedDateTime.now().withHour(22)
                 )
                 .and()
                 .path("/api/promotions/**")
                 .uri("https://promotion-service.com")
            }
            // 管理员后台(仅内网访问)
            .route("admin_service") { r ->
                r.remoteAddr("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16")
                 .and()
                 .path("/admin/**")
                 .uri("https://admin-service.com")
            }
            // 默认服务
            .route("default_service") { r ->
                r.path("/**")
                 .uri("https://default-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        # VIP用户路由到高性能服务
        - id: vip_user_service
          uri: https://vip-service.com
          predicates:
            - Cookie=membership, vip|premium
            - Path=/api/products/**,/api/orders/**
        # 移动端用户路由到移动优化服务
        - id: mobile_optimized_service
          uri: https://mobile-service.com
          predicates:
            - Header=User-Agent, .*Mobile.*
            - Path=/api/**
        # 管理员后台(仅内网访问)
        - id: admin_service
          uri: https://admin-service.com
          predicates:
            - RemoteAddr=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
            - Path=/admin/**
        # 默认服务
        - id: default_service
          uri: https://default-service.com
          predicates:
            - Path=/**

7. 最佳实践和注意事项

性能优化建议

TIP

路由顺序很重要:Spring Cloud Gateway 按照路由定义的顺序进行匹配,应将更具体的路由放在前面,通用路由放在后面。

优化路由顺序示例

kotlin
@Configuration
class OptimizedRouting {

    @Bean
    fun optimizedRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            // 具体路由放在前面
            .route("specific_api_v2") { r ->
                r.path("/api/v2/users/{userId}/orders")
                 .and()
                 .method(HttpMethod.GET)
                 .uri("https://user-order-service.com")
            }
            // 较具体的路由
            .route("api_v2_users") { r ->
                r.path("/api/v2/users/**")
                 .uri("https://user-service-v2.com")
            }
            // 较通用的路由
            .route("api_v2") { r ->
                r.path("/api/v2/**")
                 .uri("https://api-v2-gateway.com")
            }
            // 最通用的路由放在最后
            .route("fallback") { r ->
                r.path("/**")
                 .uri("https://default-service.com")
            }
            .build()
    }
}
yaml
spring:
  cloud:
    gateway:
      routes:
        # 具体路由放在前面
        - id: specific_api_v2
          uri: https://user-order-service.com
          predicates:
            - Path=/api/v2/users/{userId}/orders
            - Method=GET
        # 较具体的路由
        - id: api_v2_users
          uri: https://user-service-v2.com
          predicates:
            - Path=/api/v2/users/**
        # 较通用的路由
        - id: api_v2
          uri: https://api-v2-gateway.com
          predicates:
            - Path=/api/v2/**
        # 最通用的路由放在最后
        - id: fallback
          uri: https://default-service.com
          predicates:
            - Path=/**

调试和监控

在生产环境中,建议启用路由的调试日志和监控指标,以便及时发现和解决路由问题。

yaml
logging:
  level:
    org.springframework.cloud.gateway: DEBUG
    org.springframework.web.reactive.function.client: DEBUG

management:
  endpoints:
    web:
      exposure:
        include: health, metrics, gateway
  endpoint:
    gateway:
      enabled: true

常见陷阱和解决方案

WARNING

时区问题:使用时间相关谓词时,务必明确指定时区,避免因服务器时区不同导致的路由错误。

CAUTION

正则表达式性能:Cookie 和 Header 谓词工厂中的正则表达式会影响性能,应该使用简单、高效的正则表达式。

kotlin
// ❌ 避免复杂的正则表达式
.cookie("session", "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{32}$")

// ✅ 使用简单的模式匹配
.cookie("session", "sess_.*")

动态路由配置

对于需要动态修改路由的场景,可以结合配置中心实现:

动态路由配置示例

kotlin
@Component
@RefreshScope
class DynamicRouting {

    // 使用配置中心动态加载路由权重
    @Value("\${gateway.routes.user.weight:80}")
    private var userServiceWeight: Int = 80

    @Value("\${gateway.routes.user-v2.weight:20}")
    private var userServiceV2Weight: Int = 20

    @Bean
    @RefreshScope
    fun dynamicRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("user_service_v1") { r ->
                r.weight("user_service_group", userServiceWeight)
                 .and()
                 .path("/api/users/**")
                 .uri("https://user-service-v1.com")
            }
            .route("user_service_v2") { r ->
                r.weight("user_service_group", userServiceV2Weight)
                 .and()
                 .path("/api/users/**")
                 .uri("https://user-service-v2.com")
            }
            .build()
    }
}
yaml
gateway:
  routes:
    user:
      weight: 80
    user-v2:
      weight: 20

spring:
  cloud:
    gateway:
      routes:
        - id: user_service_v1
          uri: https://user-service-v1.com
          predicates:
            - Weight=user_service_group, ${gateway.routes.user.weight}
            - Path=/api/users/**
        - id: user_service_v2
          uri: https://user-service-v2.com
          predicates:
            - Weight=user_service_group, ${gateway.routes.user-v2.weight}
            - Path=/api/users/**

总结

Spring Cloud Gateway 的路由谓词工厂为微服务架构提供了强大而灵活的请求路由能力。通过合理使用不同类型的谓词工厂,我们可以实现:

  • 🕒 基于时间的路由:定时发布、限时活动、维护窗口
  • 🌐 基于请求属性的路由:多租户、API 版本控制、设备类型区分
  • 🔒 基于网络的路由:安全控制、地理位置路由、内外网隔离
  • ⚖️ 基于权重的路由:灰度发布、蓝绿部署、负载均衡

掌握这些谓词工厂的使用,能够帮助我们构建更加智能、灵活、可靠的微服务网关系统。在实际应用中,建议根据具体业务需求组合使用多个谓词工厂,并注意路由顺序、性能优化和监控调试等方面的最佳实践。