Skip to content

流式 Java 路由 API

概述

Spring Cloud Gateway 提供了 RouteLocatorBuilder Bean,它包含一个流式(Fluent)API,允许我们在 Java/Kotlin 中以编程方式简单地配置路由规则。

流式 API 是一种编程模式,通过方法链式调用来构建复杂的对象配置,使代码更加直观和易读。

业务场景

在微服务架构中,网关通常需要处理以下业务场景:

  • 图片服务路由:根据不同的图片格式(PNG、WebP)路由到不同的处理服务
  • 限流控制:对特定域名的请求进行流量限制
  • 响应头添加:为不同的服务添加特定的响应头信息
  • 负载均衡:将请求分发到不同的后端服务实例

流式 API 的优势

传统的配置文件方式虽然简单,但在复杂的路由规则下显得力不从心。流式 API 解决了以下问题:

  • 动态配置:可以根据运行时条件动态调整路由规则
  • 复杂逻辑:支持复杂的谓词组合(AND、OR、NOT)
  • 类型安全:编译时检查,减少配置错误
  • 代码复用:可以将常用的路由配置封装成方法

基本用法示例

图片服务路由配置

以下示例展示了如何配置一个图片处理服务的路由规则:

kotlin
import org.springframework.cloud.gateway.filter.factory.ThrottleGatewayFilterFactory
import org.springframework.cloud.gateway.route.RouteLocator
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.concurrent.TimeUnit

@Configuration
class GatewayConfiguration {

    @Bean
    fun customRouteLocator(
        builder: RouteLocatorBuilder,
        throttle: ThrottleGatewayFilterFactory
    ): RouteLocator {
        return builder.routes()
            // PNG 图片处理路由:匹配特定域名和路径
            .route { r ->
                r.host("**.abc.org")                    // 匹配所有 abc.org 子域名
                    .and().path("/image/png")            // 并且路径为 /image/png
                    .filters { f ->
                        // 添加自定义响应头,用于标识处理的服务
                        f.addResponseHeader("X-TestHeader", "foobar")
                    }
                    .uri("http://httpbin.org:80")        // 转发到目标服务
            }
            // WebP 图片处理路由:仅匹配路径
            .route { r ->
                r.path("/image/webp")                    // 匹配 WebP 图片请求
                    .filters { f ->
                        // 添加另一个自定义响应头
                        f.addResponseHeader("X-AnotherHeader", "baz")
                    }
                    .uri("http://httpbin.org:80")        // 转发到同一个服务
                    .metadata("key", "value")            // 添加路由元数据
            }
            // 限流路由:对特定域名进行流量控制
            .route { r ->
                r.order(-1)                              // 设置路由优先级(数字越小优先级越高)
                    .host("**.throttle.org")             // 匹配需要限流的域名
                    .and().path("/get")                  // 匹配 GET 请求路径
                    .filters { f ->
                        // 应用限流过滤器:1个令牌,1个令牌桶容量,10秒补充周期
                        f.filter(throttle.apply(1, 1, 10, TimeUnit.SECONDS))
                    }
                    .uri("http://httpbin.org:80")        // 转发到目标服务
                    .metadata("key", "value")            // 添加路由元数据
            }
            .build()                                     // 构建路由配置
    }
}
java
import org.springframework.cloud.gateway.filter.factory.ThrottleGatewayFilterFactory;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;

@Configuration
public class GatewayConfiguration {

    @Bean
    public RouteLocator customRouteLocator(
            RouteLocatorBuilder builder,
            ThrottleGatewayFilterFactory throttle) {
        return builder.routes()
                .route(r -> r.host("**.abc.org").and().path("/image/png")
                        .filters(f ->
                                f.addResponseHeader("X-TestHeader", "foobar"))
                        .uri("http://httpbin.org:80")
                )
                .route(r -> r.path("/image/webp")
                        .filters(f ->
                                f.addResponseHeader("X-AnotherHeader", "baz"))
                        .uri("http://httpbin.org:80")
                        .metadata("key", "value")
                )
                .route(r -> r.order(-1)
                        .host("**.throttle.org").and().path("/get")
                        .filters(f -> f.filter(throttle.apply(1,
                                1,
                                10,
                                TimeUnit.SECONDS)))
                        .uri("http://httpbin.org:80")
                        .metadata("key", "value")
                )
                .build();
    }
}

路由处理流程

以下流程图展示了请求在网关中的处理过程:

核心组件说明

RouteLocatorBuilder

RouteLocatorBuilder 是构建路由配置的核心工具类,提供了以下主要方法:

  • routes():开始构建路由规则
  • route():定义单个路由规则
  • build():完成构建并返回 RouteLocator 实例

路由谓词 (Route Predicates)

路由谓词用于匹配请求条件:

kotlin
// 主机名匹配(支持通配符)
r.host("**.abc.org")

// 路径匹配
r.path("/image/png")

// 组合条件(AND 逻辑)
r.host("**.abc.org").and().path("/image/png")

// 路由优先级
r.order(-1)

过滤器 (Filters)

过滤器用于修改请求或响应:

kotlin
// 添加响应头
f.addResponseHeader("X-TestHeader", "foobar")

// 应用自定义过滤器(如限流)
f.filter(throttle.apply(1, 1, 10, TimeUnit.SECONDS))

高级特性

谓词逻辑组合

流式 API 支持复杂的谓词逻辑组合:

kotlin
@Bean
fun advancedRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
    return builder.routes()
        .route { r ->
            r.path("/api/**")                           // 匹配 API 路径
                .and().method("GET", "POST")            // 并且是 GET 或 POST 方法
                .and().header("X-API-Key")              // 并且包含 API 密钥头
                .or().path("/public/**")                // 或者是公共路径
                .filters { f ->
                    f.addRequestHeader("X-Gateway", "spring-cloud")
                        .addResponseHeader("X-Processed-By", "gateway")
                }
                .uri("lb://backend-service")            // 负载均衡到后端服务
        }
        .build()
}

元数据配置

可以为路由添加元数据,用于监控和调试:

kotlin
.route { r ->
    r.path("/metrics/**")
        .filters { f -> f.stripPrefix(1) }           // 去除路径前缀
        .uri("http://monitoring-service:8080")
        .metadata("service-type", "monitoring")      // 服务类型元数据
        .metadata("version", "1.0")                  // 版本信息
        .metadata("timeout", 5000)                   // 超时配置
}

实际业务应用场景

1. 电商平台 API 网关

kotlin
@Bean
fun ecommerceRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
    return builder.routes()
        // 用户服务路由
        .route { r ->
            r.path("/api/users/**")
                .filters { f ->
                    f.stripPrefix(2)                     // 去除 /api/users 前缀
                        .addRequestHeader("Service", "user-service")
                }
                .uri("lb://user-service")                // 负载均衡到用户服务
        }
        // 订单服务路由
        .route { r ->
            r.path("/api/orders/**")
                .filters { f ->
                    f.stripPrefix(2)
                        .addRequestHeader("Service", "order-service")
                        .circuitBreaker { cb -> cb.name("order-cb") } // 熔断保护
                }
                .uri("lb://order-service")
        }
        // 支付服务路由(需要特殊安全处理)
        .route { r ->
            r.path("/api/payments/**")
                .and().header("Authorization")           // 必须包含认证头
                .filters { f ->
                    f.stripPrefix(2)
                        .addRequestHeader("Service", "payment-service")
                        .addRequestHeader("X-Security-Level", "high")
                }
                .uri("lb://payment-service")
        }
        .build()
}

2. 内容分发网络 (CDN)

kotlin
@Bean
fun cdnRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
    return builder.routes()
        // 静态资源路由
        .route { r ->
            r.path("/static/**")
                .filters { f ->
                    f.addResponseHeader("Cache-Control", "max-age=3600") // 缓存 1 小时
                        .addResponseHeader("X-CDN-Cache", "HIT")
                }
                .uri("http://static-file-server:8080")
        }
        // 图片优化路由
        .route { r ->
            r.path("/images/**")
                .and().header("Accept", "image/webp")    // 支持 WebP 格式的客户端
                .filters { f ->
                    f.addResponseHeader("X-Image-Format", "webp")
                        .addResponseHeader("Content-Type", "image/webp")
                }
                .uri("http://image-optimizer:8080")
        }
        .build()
}

最佳实践

以下是使用流式 API 的最佳实践建议:

1. 路由优先级管理

kotlin
// 使用 order() 方法明确指定路由优先级
.route { r ->
    r.order(-100)                    // 最高优先级
        .path("/api/admin/**")
        // ... 管理员路由配置
}
.route { r ->
    r.order(0)                       // 默认优先级
        .path("/api/**")
        // ... 普通 API 路由配置
}

2. 过滤器链优化

kotlin
.filters { f ->
    f.addRequestHeader("X-Request-ID", UUID.randomUUID().toString()) // 请求追踪
        .addRequestHeader("X-Gateway-Time", Instant.now().toString())  // 网关时间戳
        .circuitBreaker { cb -> cb.name("default-cb") }                // 熔断保护
        .retry { retry -> retry.retries(3) }                           // 重试机制
        .addResponseHeader("X-Response-Time", "#{T(System).currentTimeMillis()}") // 响应时间
}

3. 配置模块化

kotlin
@Configuration
class GatewayRoutesConfiguration {

    @Bean
    fun userServiceRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route(this::configureUserRoute)
            .build()
    }

    @Bean
    fun orderServiceRoutes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route(this::configureOrderRoute)
            .build()
    }

    private fun configureUserRoute(r: RouteSpec): Route {
        return r.path("/users/**")
            .filters { f -> f.stripPrefix(1) }
            .uri("lb://user-service")
            .build()
    }

    private fun configureOrderRoute(r: RouteSpec): Route {
        return r.path("/orders/**")
            .filters { f -> f.stripPrefix(1) }
            .uri("lb://order-service")
            .build()
    }
}

常见问题

Q: 如何调试路由配置?

可以通过以下方式调试路由配置:

  1. 启用调试日志

    yaml
    logging:
      level:
        org.springframework.cloud.gateway: DEBUG
  2. 添加自定义过滤器记录日志

    kotlin
    .filters { f ->
        f.filter { exchange, chain ->
            logger.info("Processing request: ${exchange.request.uri}")
            chain.filter(exchange)
        }
    }

Q: 路由优先级如何确定?

路由优先级规则:

  • 数字越小,优先级越高
  • 未指定 order 的路由默认优先级为 0
  • 相同优先级的路由按定义顺序执行

Q: 如何处理路由冲突?

避免路由冲突的方法:

  • 使用更具体的谓词条件
  • 合理设置路由优先级
  • 使用 .and() 组合多个条件增加精确性

总结

Spring Cloud Gateway 的流式 Java API 提供了强大而灵活的路由配置能力。通过编程方式配置路由,我们可以:

  • ✅ 实现复杂的路由逻辑
  • ✅ 支持动态路由配置
  • ✅ 提供类型安全的配置方式
  • ✅ 便于测试和维护

在实际项目中,建议结合配置文件和编程配置两种方式,将简单的路由规则放在配置文件中,复杂的业务逻辑使用流式 API 实现。

Details

扩展阅读