Appearance
网关配置
Spring Cloud Gateway 是基于 Spring WebFlux 构建的 API 网关,它为微服务架构提供了一种简单而有效的方式来路由和过滤请求。在微服务环境中,网关作为系统的统一入口,负责请求路由、负载均衡、安全认证、限流等功能。
Spring Cloud Gateway 的配置是通过 `RouteDefinitionLocator` 实例的集合来驱动的,这是理解网关配置的核心概念。
路由定义定位器(RouteDefinitionLocator)
核心接口
RouteDefinitionLocator 是 Spring Cloud Gateway 配置的核心接口,它负责提供路由定义信息。
kotlin
import reactor.core.publisher.Flux
/**
* 路由定义定位器接口
* 负责提供路由定义的响应式流
*/
interface RouteDefinitionLocator {
/**
* 获取所有路由定义
* @return 路由定义的响应式流
*/
fun getRouteDefinitions(): Flux<RouteDefinition>
}java
public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}默认实现
默认情况下,Spring Cloud Gateway 使用 PropertiesRouteDefinitionLocator 来加载配置属性,它利用 Spring Boot 的 @ConfigurationProperties 机制。
kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.cloud.gateway.route.RouteDefinition
import reactor.core.publisher.Flux
/**
* 基于属性文件的路由定义定位器
* 通过 Spring Boot 的配置属性机制加载路由配置
*/
@ConfigurationProperties("spring.cloud.gateway")
class PropertiesRouteDefinitionLocator : RouteDefinitionLocator {
private val routes = mutableListOf<RouteDefinition>()
override fun getRouteDefinitions(): Flux<RouteDefinition> {
return Flux.fromIterable(routes)
}
// getter 和 setter 方法
fun getRoutes(): List<RouteDefinition> = routes
fun setRoutes(routes: List<RouteDefinition>) {
this.routes.clear()
this.routes.addAll(routes)
}
}配置方式
YAML 配置
Spring Cloud Gateway 支持两种配置语法:完整语法和简化语法。以下是两种等价的配置示例:
yaml
spring:
cloud:
gateway:
routes:
# 完整语法配置
- id: setstatus_route
uri: https://example.org
filters:
- name: SetStatus
args:
status: 401
# 简化语法配置
- id: setstatusshortcut_route
uri: https://example.org
filters:
- SetStatus=401配置结构图解
实际业务场景示例
在电商系统中,我们可能需要配置多个路由来处理不同的业务模块:
kotlin
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
/**
* 电商系统网关路由配置
* 解决了多个微服务的统一入口和路由分发问题
*/
@Configuration
class EcommerceGatewayConfig {
/**
* 通过编程方式配置路由
* 适用于需要动态路由或复杂路由逻辑的场景
*/
@Bean
fun customRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
// 用户服务路由
.route("user-service") { r ->
r.path("/api/users/**")
.filters { f ->
f.stripPrefix(1) // 移除 /api 前缀
.addRequestHeader("X-Service", "user-service")
}
.uri("lb://user-service") // 负载均衡到用户服务
}
// 商品服务路由
.route("product-service") { r ->
r.path("/api/products/**")
.and()
.method("GET", "POST") // 只允许 GET 和 POST 请求
.filters { f ->
f.stripPrefix(1)
.circuitBreaker { config ->
config.name = "product-circuit-breaker"
config.fallbackUri = "forward:/fallback/products"
}
}
.uri("lb://product-service")
}
// 订单服务路由(需要认证)
.route("order-service") { r ->
r.path("/api/orders/**")
.filters { f ->
f.stripPrefix(1)
.requestRateLimiter { config ->
config.rateLimiter = redisRateLimiter()
config.keyResolver = userKeyResolver()
}
}
.uri("lb://order-service")
}
.build()
}
/**
* Redis 限流器配置
* 用于控制用户请求频率
*/
@Bean
fun redisRateLimiter(): RedisRateLimiter {
return RedisRateLimiter(10, 20) // 每秒10个请求,突发20个
}
/**
* 用户键解析器
* 根据用户ID进行限流
*/
@Bean
fun userKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
exchange.request.headers.getFirst("X-User-Id")?.let {
Mono.just(it)
} ?: Mono.just("anonymous")
}
}
}在生产环境中,建议使用编程方式配置复杂路由,而简单路由可以使用 YAML 配置,这样既保持了灵活性又提高了可维护性。
动态配置源
数据库配置
对于生产环境,从外部数据源加载配置通常更加灵活。Spring Cloud Gateway 的未来版本将支持基于 Spring Data Repository 的实现:
kotlin
import org.springframework.data.repository.reactive.ReactiveCrudRepository
import org.springframework.stereotype.Repository
import reactor.core.publisher.Flux
/**
* 路由定义数据访问接口
* 支持从数据库动态加载路由配置
*/
@Repository
interface RouteDefinitionRepository : ReactiveCrudRepository<RouteDefinitionEntity, String> {
/**
* 查找启用的路由定义
* @return 启用的路由定义流
*/
fun findByEnabledTrue(): Flux<RouteDefinitionEntity>
/**
* 根据服务名查找路由
* @param serviceName 服务名称
* @return 匹配的路由定义流
*/
fun findByServiceName(serviceName: String): Flux<RouteDefinitionEntity>
}
/**
* 基于数据库的路由定义定位器
* 解决了路由配置的动态管理问题
*/
@Component
class DatabaseRouteDefinitionLocator(
private val routeRepository: RouteDefinitionRepository
) : RouteDefinitionLocator {
override fun getRouteDefinitions(): Flux<RouteDefinition> {
return routeRepository.findByEnabledTrue()
.map { entity -> convertToRouteDefinition(entity) }
.doOnError { error ->
log.error("Failed to load route definitions from database", error)
}
}
/**
* 将数据库实体转换为路由定义
*/
private fun convertToRouteDefinition(entity: RouteDefinitionEntity): RouteDefinition {
val routeDefinition = RouteDefinition()
routeDefinition.id = entity.id
routeDefinition.uri = URI.create(entity.uri)
// 解析断言配置
entity.predicates?.let { predicatesJson ->
routeDefinition.predicates = parsePredicates(predicatesJson)
}
// 解析过滤器配置
entity.filters?.let { filtersJson ->
routeDefinition.filters = parseFilters(filtersJson)
}
return routeDefinition
}
}Redis 配置
kotlin
import org.springframework.data.redis.core.ReactiveRedisTemplate
import org.springframework.stereotype.Component
import com.fasterxml.jackson.databind.ObjectMapper
/**
* 基于 Redis 的路由定义定位器
* 支持分布式环境下的路由配置共享
*/
@Component
class RedisRouteDefinitionLocator(
private val redisTemplate: ReactiveRedisTemplate<String, String>,
private val objectMapper: ObjectMapper
) : RouteDefinitionLocator {
companion object {
private const val ROUTES_KEY = "gateway:routes"
}
override fun getRouteDefinitions(): Flux<RouteDefinition> {
return redisTemplate.opsForHash<String, String>()
.values(ROUTES_KEY)
.map { json -> objectMapper.readValue(json, RouteDefinition::class.java) }
.doOnError { error ->
log.error("Failed to load route definitions from Redis", error)
}
}
/**
* 添加或更新路由定义
* @param routeDefinition 路由定义
*/
fun saveRouteDefinition(routeDefinition: RouteDefinition): Mono<Boolean> {
val json = objectMapper.writeValueAsString(routeDefinition)
return redisTemplate.opsForHash<String, String>()
.put(ROUTES_KEY, routeDefinition.id, json)
}
/**
* 删除路由定义
* @param routeId 路由ID
*/
fun deleteRouteDefinition(routeId: String): Mono<Long> {
return redisTemplate.opsForHash<String, String>()
.remove(ROUTES_KEY, routeId)
}
}路由定义监控
启用监控指标
要启用 RouteDefinition 监控指标,需要添加 Spring Boot Actuator 依赖:
kotlin
// build.gradle.kts
dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.cloud:spring-cloud-starter-gateway")
}配置监控
yaml
spring:
cloud:
gateway:
# 启用网关监控指标
metrics:
enabled: true
routes:
# 路由配置...
management:
endpoints:
web:
exposure:
include: health,info,metrics,gateway
endpoint:
gateway:
enabled: true
metrics:
enabled: true监控指标说明
启用监控后,将提供以下指标:
| 指标名称 | 描述 | 访问路径 |
|---|---|---|
spring.cloud.gateway.routes.count | 当前路由定义数量 | /actuator/metrics/spring.cloud.gateway.routes.count |
gateway.requests | 网关请求计数 | /actuator/metrics/gateway.requests |
gateway.request.duration | 请求处理时间 | /actuator/metrics/gateway.request.duration |
自定义监控指标
kotlin
import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Timer
import org.springframework.stereotype.Component
/**
* 网关监控指标收集器
* 提供自定义的业务监控指标
*/
@Component
class GatewayMetricsCollector(
private val meterRegistry: MeterRegistry
) {
private val routeTimer = Timer.builder("gateway.route.duration")
.description("Route processing time")
.register(meterRegistry)
private val errorCounter = meterRegistry.counter("gateway.route.errors")
/**
* 记录路由处理时间
*/
fun recordRouteTime(routeId: String, duration: Long) {
Timer.Sample.start(meterRegistry)
.stop(Timer.builder("gateway.route.duration")
.tag("route", routeId)
.register(meterRegistry))
}
/**
* 记录路由错误
*/
fun recordRouteError(routeId: String, errorType: String) {
meterRegistry.counter("gateway.route.errors",
"route", routeId,
"error", errorType)
.increment()
}
}配置最佳实践
1. 环境隔离配置
yaml
# application.yml
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
---
# 开发环境配置
spring:
config:
activate:
on-profile: dev
cloud:
gateway:
routes:
- id: user-service-dev
uri: http://localhost:8081
predicates:
- Path=/api/users/**
---
# 生产环境配置
spring:
config:
activate:
on-profile: prod
cloud:
gateway:
routes:
- id: user-service-prod
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: CircuitBreaker
args:
name: user-service-cb
fallbackUri: forward:/fallback/users2. 配置加密
kotlin
import org.springframework.cloud.context.config.annotation.RefreshScope
import org.springframework.boot.context.properties.ConfigurationProperties
/**
* 网关安全配置
* 支持配置刷新和敏感信息加密
*/
@RefreshScope
@ConfigurationProperties(prefix = "gateway.security")
data class GatewaySecurityProperties(
var jwtSecret: String = "",
var apiKeys: Map<String, String> = emptyMap(),
var corsAllowedOrigins: List<String> = emptyList()
)在生产环境中,敏感配置信息(如 JWT 密钥、API 密钥)应该使用配置加密或外部密钥管理服务。
3. 配置验证
kotlin
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component
/**
* 网关配置验证器
* 在应用启动时验证路由配置的正确性
*/
@Component
class GatewayConfigValidator(
private val routeDefinitionLocator: RouteDefinitionLocator
) {
@EventListener(ApplicationReadyEvent::class)
fun validateRouteConfigurations() {
routeDefinitionLocator.getRouteDefinitions()
.doOnNext { route ->
validateRoute(route)
}
.subscribe(
{ log.info("Route validation completed successfully") },
{ error -> log.error("Route validation failed", error) }
)
}
private fun validateRoute(route: RouteDefinition) {
// 验证路由ID唯一性
require(route.id.isNotBlank()) { "Route ID cannot be blank" }
// 验证URI格式
require(route.uri != null) { "Route URI cannot be null for route: ${route.id}" }
// 验证断言配置
require(route.predicates.isNotEmpty()) { "Route must have at least one predicate: ${route.id}" }
log.info("Route ${route.id} validation passed")
}
}总结
Spring Cloud Gateway 的配置系统具有以下特点:
- 灵活性:支持多种配置源(属性文件、数据库、Redis 等)
- 可扩展性:通过
RouteDefinitionLocator接口可以自定义配置加载逻辑 - 监控能力:内置监控指标,便于运维管理
- 动态性:支持运行时配置更新和路由刷新
选择合适的配置方式取决于具体的业务场景:
- 简单场景使用 YAML 配置
- 需要动态管理时使用数据库配置
- 分布式环境推荐使用 Redis 配置
- 复杂路由逻辑建议使用编程方式配置
通过合理的配置策略,Spring Cloud Gateway 可以很好地解决微服务架构中的 API 网关需求,提供统一的服务入口和强大的路由管理能力。