Appearance
RequestRateLimiter GatewayFilter Factory
概述
RequestRateLimiter GatewayFilter Factory 是 Spring Cloud Gateway 提供的一个核心限流过滤器,用于控制通过网关的请求频率。当请求超过设定的限制时,过滤器会返回 HTTP 429 - Too Many Requests 状态码,有效保护下游服务免受流量冲击。
解决的业务问题
在微服务架构中,RequestRateLimiter 过滤器主要解决以下关键问题:
1. API 流量控制
- 防止服务过载:限制单位时间内的请求数量,避免下游服务因流量过大而崩溃
- 保障服务稳定性:通过合理的限流策略确保系统在高并发场景下的稳定运行
- 资源合理分配:根据用户等级、API 重要性等维度进行差异化限流
2. 安全防护
- 防止 DDoS 攻击:有效抵御恶意用户的大量请求攻击
- 防止接口滥用:避免用户无节制地调用昂贵的 API 接口
- 保护付费服务:确保付费用户享受到应有的服务质量
3. 商业价值保护
- SaaS 服务分级:根据用户订阅等级提供不同的请求配额
- API 货币化:为不同价格的 API 套餐设置相应的调用限制
- 公平使用原则:确保所有用户都能公平地使用系统资源
工作原理
核心组件
1. KeyResolver 接口
KeyResolver 用于确定限流的维度,决定如何识别和区分不同的请求来源。
kotlin
// KeyResolver 接口定义
interface KeyResolver {
fun resolve(exchange: ServerWebExchange): Mono<String>
}2. 内置 KeyResolver 实现
kotlin
@Configuration
class KeyResolverConfig {
/**
* 基于用户ID的限流策略
* 适用场景: 用户级别的API限流
*/
@Bean
fun userKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
// 从查询参数中获取用户ID
val userId = exchange.request.queryParams.getFirst("userId")
Mono.justOrEmpty(userId)
}
}
/**
* 基于IP地址的限流策略
* 适用场景: 防止恶意IP攻击
*/
@Bean
fun ipKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
// 获取真实客户端IP
val remoteAddress = exchange.request.remoteAddress?.address?.hostAddress
Mono.justOrEmpty(remoteAddress)
}
}
/**
* 基于API路径的限流策略
* 适用场景: 不同API接口的差异化限流
*/
@Bean
fun pathKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
// 基于请求路径进行限流
val path = exchange.request.path.value()
Mono.just(path)
}
}
/**
* 基于认证用户的限流策略
* 适用场景: 需要用户认证的API
*/
@Bean
fun principalKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
// 从认证信息中获取用户名
exchange.getPrincipal<Principal>()
.cast(Principal::class.java)
.map { it.name }
.switchIfEmpty(Mono.empty())
}
}
/**
* 复合限流策略
* 适用场景: 需要多维度限流控制
*/
@Bean
fun compositeKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
val request = exchange.request
// 优先使用用户ID,其次使用IP
val userId = request.headers.getFirst("X-User-Id")
val clientIp = request.remoteAddress?.address?.hostAddress
when {
!userId.isNullOrEmpty() -> Mono.just("user:$userId")
!clientIp.isNullOrEmpty() -> Mono.just("ip:$clientIp")
else -> Mono.just("anonymous")
}
}
}
}Redis RateLimiter
Redis 实现基于令牌桶算法,是最常用的限流方案。
核心参数
| 参数 | 说明 | 默认值 | 示例值 |
|---|---|---|---|
replenishRate | 每秒令牌补充速率 | 无 | 10 |
burstCapacity | 令牌桶最大容量 | 无 | 20 |
requestedTokens | 每个请求消耗的令牌数 | 1 | 1 |
IMPORTANT
令牌桶算法原理:
replenishRate: 控制稳定的请求速率burstCapacity: 允许短时间的突发流量requestedTokens: 不同接口可以设置不同的"价格"
配置示例
yaml
spring:
cloud:
gateway:
routes:
- id: user_api_route
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒补充10个令牌
redis-rate-limiter.burstCapacity: 20 # 令牌桶最大容量20
redis-rate-limiter.requestedTokens: 1 # 每个请求消耗1个令牌
key-resolver: "#{@userKeyResolver}" # 使用用户级别限流kotlin
@Configuration
class RateLimiterGatewayConfig {
@Bean
fun rateLimiterRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
.route("user_api_with_rate_limit") { r ->
r.path("/api/users/**")
.filters { f ->
f.requestRateLimiter { config ->
// Redis 限流器配置
config.rateLimiter = redisRateLimiter()
config.keyResolver = userKeyResolver()
// 设置限流参数
config.setReplenishRate(10) // 每秒补充10个令牌
config.setBurstCapacity(20) // 最大容量20个令牌
config.setRequestedTokens(1) // 每请求消耗1个令牌
}
}
.uri("lb://user-service")
}
.build()
}
@Bean
@Primary
fun redisRateLimiter(
redisTemplate: ReactiveStringRedisTemplate,
applicationEventPublisher: ApplicationEventPublisher
): RedisRateLimiter {
return RedisRateLimiter(redisTemplate, "request_rate_limiter", applicationEventPublisher)
}
}高级配置场景
yaml
spring:
cloud:
gateway:
routes:
# 免费用户 - 严格限流
- id: free_user_api
uri: lb://user-service
predicates:
- Path=/api/free/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 5 # 每秒5个请求
redis-rate-limiter.burstCapacity: 10 # 最大突发10个
key-resolver: "#{@userKeyResolver}"
# VIP用户 - 宽松限流
- id: vip_user_api
uri: lb://user-service
predicates:
- Path=/api/vip/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100 # 每秒100个请求
redis-rate-limiter.burstCapacity: 200 # 最大突发200个
key-resolver: "#{@userKeyResolver}"
# 文件上传 - 高成本操作
- id: file_upload_api
uri: lb://file-service
predicates:
- Path=/api/upload/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 2 # 每秒2个请求
redis-rate-limiter.burstCapacity: 5 # 最大突发5个
redis-rate-limiter.requestedTokens: 5 # 每个请求消耗5个令牌
key-resolver: "#{@userKeyResolver}"kotlin
@Configuration
class AdvancedRateLimiterConfig {
/**
* 多维度限流配置
*/
@Bean
fun multiDimensionRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
// API级别的基础限流
.route("api_global_limit") { r ->
r.path("/api/**")
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = redisRateLimiter()
config.keyResolver = ipKeyResolver() // IP级别限流
config.setReplenishRate(1000) // 全局每秒1000个请求
config.setBurstCapacity(2000)
}
}
.uri("lb://api-service")
}
// 用户级别的精细限流
.route("user_specific_limit") { r ->
r.path("/api/users/**")
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = redisRateLimiter()
config.keyResolver = userTierKeyResolver() // 用户等级限流
config.setReplenishRate(50)
config.setBurstCapacity(100)
}
}
.uri("lb://user-service")
}
.build()
}
/**
* 基于用户等级的限流策略
*/
@Bean
fun userTierKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
val request = exchange.request
val userId = request.headers.getFirst("X-User-Id")
val userTier = request.headers.getFirst("X-User-Tier") ?: "free"
when {
!userId.isNullOrEmpty() -> Mono.just("user:$userTier:$userId")
else -> {
val clientIp = request.remoteAddress?.address?.hostAddress
Mono.justOrEmpty(clientIp?.let { "ip:$it" })
}
}
}
}
}Bucket4j RateLimiter
Bucket4j 是一个基于令牌桶算法的 Java 限流库,提供了更灵活的配置选项。
依赖配置
kotlin
// build.gradle.kts
dependencies {
implementation("com.github.ben-manes.caffeine:caffeine")
implementation("com.bucket4j:bucket4j_jdk17-core")
implementation("com.bucket4j:bucket4j_jdk17-caffeine")
}核心配置
kotlin
@Configuration
class Bucket4jRateLimiterConfig {
/**
* Caffeine 本地缓存代理管理器
* 适用于单实例部署或测试环境
*/
@Bean
fun caffeineProxyManager(): AsyncProxyManager<String> {
val builder = Caffeine.newBuilder()
.maximumSize(100_000) // 最大缓存100,000个桶
.expireAfterWrite(Duration.ofMinutes(10)) // 10分钟后过期
return CaffeineProxyManager(builder, Duration.ofMinutes(1)).asAsync()
}
/**
* Redis 分布式代理管理器
* 适用于集群部署环境
*/
@Bean
fun redisProxyManager(
@Qualifier("reactiveRedisTemplate") redisTemplate: ReactiveRedisTemplate<String, ByteArray>
): AsyncProxyManager<String> {
return RedisProxyManager.builderFor(redisTemplate)
.withKeyPrefix("rate_limit:")
.build()
.asAsync()
}
@Bean
fun bucket4jRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
.route("bucket4j_api_limit") { r ->
r.path("/api/bucket4j/**")
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = bucket4jRateLimiter()
config.keyResolver = userKeyResolver()
}
}
.uri("lb://api-service")
}
.build()
}
@Bean
fun bucket4jRateLimiter(): RateLimiter<Any> {
return Bucket4jRateLimiter()
}
}yaml
spring:
cloud:
gateway:
routes:
- id: bucket4j_rate_limited_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
bucket4j-rate-limiter.capacity: 20 # 桶容量
bucket4j-rate-limiter.refillTokens: 10 # 补充令牌数
bucket4j-rate-limiter.refillPeriod: 1s # 补充周期
bucket4j-rate-limiter.requestedTokens: 1 # 每请求消耗令牌
bucket4j-rate-limiter.refillStyle: GREEDY # 补充策略
key-resolver: "#{@userKeyResolver}"补充策略对比
| 策略 | 说明 | 适用场景 |
|---|---|---|
GREEDY | 尽快补充令牌 | 一般 API 限流 |
INTERVALLY | 等待完整周期后补充 | 严格的时间窗口限流 |
INTERVALLY_ALIGNED | 按指定时间对齐补充 | 需要精确时间对齐的场景 |
实际业务场景示例
场景 1: 电商平台 API 网关
kotlin
@Configuration
class ECommerceRateLimiterConfig {
@Bean
fun ecommerceRateLimitRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
// 商品浏览 - 高频低成本
.route("product_browse") { r ->
r.path("/api/products/**")
.and().method(HttpMethod.GET)
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = redisRateLimiter()
config.keyResolver = userKeyResolver()
config.setReplenishRate(100) // 每秒100个请求
config.setBurstCapacity(200) // 允许突发200个
}
}
.uri("lb://product-service")
}
// 订单创建 - 重要业务操作
.route("order_creation") { r ->
r.path("/api/orders/**")
.and().method(HttpMethod.POST)
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = redisRateLimiter()
config.keyResolver = userKeyResolver()
config.setReplenishRate(10) // 每秒10个订单
config.setBurstCapacity(20) // 允许突发20个
config.setRequestedTokens(2) // 每个订单消耗2个令牌
}
}
.uri("lb://order-service")
}
// 支付接口 - 高成本操作
.route("payment_processing") { r ->
r.path("/api/payments/**")
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = redisRateLimiter()
config.keyResolver = userKeyResolver()
config.setReplenishRate(5) // 每秒5个支付
config.setBurstCapacity(10) // 允许突发10个
config.setRequestedTokens(5) // 每个支付消耗5个令牌
}
}
.uri("lb://payment-service")
}
.build()
}
}场景 2: SaaS 平台分级限流
kotlin
@Configuration
class SaaSRateLimiterConfig {
@Bean
fun saasRateLimitRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
// 免费版用户
.route("free_tier_api") { r ->
r.path("/api/**")
.and().header("X-User-Tier", "free")
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = redisRateLimiter()
config.keyResolver = userTierKeyResolver()
config.setReplenishRate(10) // 每秒10个请求
config.setBurstCapacity(15) // 最大突发15个
}
}
.uri("lb://api-service")
}
// 专业版用户
.route("pro_tier_api") { r ->
r.path("/api/**")
.and().header("X-User-Tier", "pro")
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = redisRateLimiter()
config.keyResolver = userTierKeyResolver()
config.setReplenishRate(100) // 每秒100个请求
config.setBurstCapacity(200) // 最大突发200个
}
}
.uri("lb://api-service")
}
// 企业版用户
.route("enterprise_tier_api") { r ->
r.path("/api/**")
.and().header("X-User-Tier", "enterprise")
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = redisRateLimiter()
config.keyResolver = userTierKeyResolver()
config.setReplenishRate(1000) // 每秒1000个请求
config.setBurstCapacity(2000) // 最大突发2000个
}
}
.uri("lb://api-service")
}
.build()
}
@Bean
fun userTierKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
val userId = exchange.request.headers.getFirst("X-User-Id")
val userTier = exchange.request.headers.getFirst("X-User-Tier") ?: "free"
Mono.justOrEmpty(userId?.let { "tier:$userTier:user:$it" })
}
}
}场景 3: 时间段差异化限流
kotlin
@Configuration
class TimeBasedRateLimiterConfig {
@Bean
fun timeBasedKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
val userId = exchange.request.headers.getFirst("X-User-Id")
val hour = LocalTime.now().hour
// 根据时间段调整限流策略
val timeSlot = when (hour) {
in 9..11, in 14..16 -> "peak" // 高峰期
in 12..13 -> "lunch" // 午休期
else -> "normal" // 正常期
}
Mono.justOrEmpty(userId?.let { "time:$timeSlot:user:$it" })
}
}
@Bean
fun timeBasedRoutes(builder: RouteLocatorBuilder): RouteLocator {
return builder.routes()
.route("time_based_api") { r ->
r.path("/api/**")
.filters { f ->
f.requestRateLimiter { config ->
config.rateLimiter = timeBasedRateLimiter()
config.keyResolver = timeBasedKeyResolver()
}
}
.uri("lb://api-service")
}
.build()
}
@Bean
fun timeBasedRateLimiter(): RateLimiter<Any> {
return object : RateLimiter<Any> {
private val redisRateLimiter = RedisRateLimiter(
reactiveRedisTemplate(),
"time_based_limiter",
applicationEventPublisher()
)
override fun isAllowed(
routeId: String,
id: String
): Mono<Response> {
val hour = LocalTime.now().hour
// 根据时间段动态调整限流参数
val (replenishRate, burstCapacity) = when (hour) {
in 9..11, in 14..16 -> Pair(50, 100) // 高峰期收紧
in 12..13 -> Pair(200, 400) // 午休期放宽
else -> Pair(100, 200) // 正常期标准
}
redisRateLimiter.config[routeId] = RedisRateLimiter.Config()
.setReplenishRate(replenishRate)
.setBurstCapacity(burstCapacity)
return redisRateLimiter.isAllowed(routeId, id)
}
}
}
}自定义 RateLimiter
kotlin
@Component
class CustomRateLimiter : RateLimiter<String> {
private val logger = LoggerFactory.getLogger(CustomRateLimiter::class.java)
private val redisTemplate: ReactiveStringRedisTemplate = TODO("注入Redis模板")
override fun isAllowed(routeId: String, id: String): Mono<Response> {
return checkRateLimit(routeId, id)
.doOnNext { response ->
// 记录限流日志
if (!response.isAllowed) {
logger.warn("Rate limit exceeded for route: $routeId, id: $id")
}
}
.doOnError { error ->
logger.error("Rate limit check failed for route: $routeId, id: $id", error)
}
}
private fun checkRateLimit(routeId: String, id: String): Mono<Response> {
val key = "rate_limit:$routeId:$id"
val windowSize = Duration.ofMinutes(1)
val maxRequests = getMaxRequestsForRoute(routeId)
return redisTemplate.opsForZSet()
.removeRangeByScore(key, 0.0, (System.currentTimeMillis() - windowSize.toMillis()).toDouble())
.then(redisTemplate.opsForZSet().count(key, 0.0, Double.MAX_VALUE))
.flatMap { currentCount ->
if (currentCount < maxRequests) {
// 允许请求,记录当前时间戳
val score = System.currentTimeMillis().toDouble()
redisTemplate.opsForZSet().add(key, score.toString(), score)
.then(redisTemplate.expire(key, windowSize))
.then(Mono.just(Response(true, mapOf(
"X-RateLimit-Remaining" to (maxRequests - currentCount - 1).toString(),
"X-RateLimit-Limit" to maxRequests.toString()
))))
} else {
// 拒绝请求
Mono.just(Response(false, mapOf(
"X-RateLimit-Remaining" to "0",
"X-RateLimit-Limit" to maxRequests.toString(),
"X-RateLimit-Retry-After" to windowSize.seconds.toString()
)))
}
}
}
private fun getMaxRequestsForRoute(routeId: String): Long {
// 根据路由ID返回不同的限制
return when (routeId) {
"high_priority_route" -> 1000
"normal_route" -> 100
"low_priority_route" -> 10
else -> 50
}
}
}错误处理与监控
自定义错误响应
kotlin
@Component
class RateLimitExceptionHandler : ResponseEntityExceptionHandler() {
@ExceptionHandler(RateLimitExceededException::class)
fun handleRateLimit(ex: RateLimitExceededException): ResponseEntity<Map<String, Any>> {
val response = mapOf(
"timestamp" to Instant.now().toString(),
"status" to 429,
"error" to "Too Many Requests",
"message" to "请求频率超过限制,请稍后重试",
"code" to "RATE_LIMIT_EXCEEDED",
"retryAfter" to ex.retryAfter,
"limit" to ex.limit,
"remaining" to 0
)
val headers = HttpHeaders().apply {
set("X-RateLimit-Limit", ex.limit.toString())
set("X-RateLimit-Remaining", "0")
set("X-RateLimit-Retry-After", ex.retryAfter.toString())
}
return ResponseEntity.status(429).headers(headers).body(response)
}
}限流监控指标
kotlin
@Component
class RateLimitMetrics {
private val meterRegistry = Metrics.globalRegistry
private val rateLimitCounter = Counter.builder("gateway.rate.limit.requests")
.description("Rate limited requests count")
.register(meterRegistry)
private val rateLimitGauge = Gauge.builder("gateway.rate.limit.current")
.description("Current rate limit usage")
.register(meterRegistry, this) { getCurrentUsage() }
@EventListener
fun handleRateLimitEvent(event: RateLimitEvent) {
// 记录限流事件
rateLimitCounter.increment(
Tags.of(
Tag.of("route", event.routeId),
Tag.of("key", event.key),
Tag.of("allowed", event.isAllowed.toString())
)
)
// 记录详细日志
if (!event.isAllowed) {
MDC.put("routeId", event.routeId)
MDC.put("key", event.key)
MDC.put("limit", event.limit.toString())
log.warn("Rate limit exceeded: route={}, key={}, limit={}",
event.routeId, event.key, event.limit)
MDC.clear()
}
}
private fun getCurrentUsage(): Double {
// 返回当前限流使用率
return 0.0 // 实际实现中应该从Redis获取
}
companion object {
private val log = LoggerFactory.getLogger(RateLimitMetrics::class.java)
}
}配置最佳实践
1. 合理设置限流参数
TIP
参数设置建议:
- replenishRate: 基于 99%用户的正常使用频率
- burstCapacity: 通常设置为 replenishRate 的 1.5-2 倍
- requestedTokens: 根据接口成本调整,昂贵操作设置更高值
2. KeyResolver 选择策略
kotlin
@Configuration
class KeyResolverStrategy {
/**
* 生产环境推荐的复合策略
*/
@Bean
@Primary
fun productionKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
val request = exchange.request
// 1. 优先使用认证用户ID
exchange.getPrincipal<JwtAuthenticationToken>()
.cast(JwtAuthenticationToken::class.java)
.map { "user:${it.token.subject}" }
.switchIfEmpty(
// 2. 其次使用API Key
Mono.justOrEmpty(request.headers.getFirst("X-API-Key"))
.map { "api:$it" }
)
.switchIfEmpty(
// 3. 最后使用IP地址
Mono.justOrEmpty(request.remoteAddress?.address?.hostAddress)
.map { "ip:$it" }
)
.switchIfEmpty(Mono.just("anonymous"))
}
}
}3. 监控告警配置
yaml
# application.yml
management:
endpoints:
web:
exposure:
include: metrics,health,info
metrics:
export:
prometheus:
enabled: true
tags:
application: gateway
service: rate-limiter
# 告警规则示例 (Prometheus)
# rate_limit_exceeded_rate > 0.1 表示超过10%的请求被限流4. 性能优化
WARNING
性能注意事项:
- Redis 连接池要配置充足
- 避免在 KeyResolver 中进行复杂计算
- 考虑使用本地缓存减少 Redis 访问
- 定期清理过期的限流数据
故障排查
常见问题与解决方案
Details
点击查看故障排查指南
问题 1: 所有请求都被限流
可能原因:
- KeyResolver 返回空值或相同值
- 限流参数设置过于严格
- Redis 连接问题
排查步骤:
bash
# 1. 检查Redis连接
redis-cli ping
# 2. 查看限流数据
redis-cli keys "request_rate_limiter*"
# 3. 检查具体限流状态
redis-cli eval "return redis.call('hmget', KEYS[1], 'tokens', 'last_refill')" 1 request_rate_limiter.{user_123}.timestamp问题 2: 限流不生效
可能原因:
- 过滤器配置错误
- KeyResolver 配置问题
- 限流参数过大
解决方案:
kotlin
// 添加调试日志
@Bean
fun debugKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
userKeyResolver().resolve(exchange)
.doOnNext { key ->
log.debug("Rate limit key resolved: $key")
}
.doOnEmpty {
log.warn("Rate limit key is empty")
}
}
}问题 3: 性能问题
优化方案:
kotlin
// 使用本地缓存优化
@Bean
fun cachedKeyResolver(): KeyResolver {
val cache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(Duration.ofMinutes(5))
.build<String, String>()
return KeyResolver { exchange ->
val requestInfo = "${exchange.request.method}-${exchange.request.path}"
val cachedKey = cache.getIfPresent(requestInfo)
if (cachedKey != null) {
Mono.just(cachedKey)
} else {
userKeyResolver().resolve(exchange)
.doOnNext { key -> cache.put(requestInfo, key) }
}
}
}总结
RequestRateLimiter 是 Spring Cloud Gateway 中非常重要的限流组件,通过合理的配置和使用,可以有效保护后端服务,提升系统的稳定性和可用性。
NOTE
关键要点:
- 选择合适的 KeyResolver 策略是限流成功的关键
- Redis RateLimiter 适合大多数生产环境
- Bucket4j 提供更灵活的配置选项
- 自定义 RateLimiter 可以实现复杂的业务逻辑
- 完善的监控和告警机制不可或缺
在实际项目中,建议根据业务需求选择合适的限流策略,并结合监控数据持续优化限流参数,确保在保护系统的同时不影响正常用户的使用体验。