跳到主要内容
版本:5.0.1

RequestRateLimiter GatewayFilter 工厂

DeepSeek V3 中英对照 RequestRateLimiter GatewayFilter Factory RequestRateLimiter GatewayFilter Factory

RequestRateLimiter GatewayFilter 工厂使用 RateLimiter 实现来确定是否允许当前请求继续执行。如果不允许,则返回 HTTP 429 - Too Many Requests 状态(默认情况下)。

此过滤器接受一个可选的 keyResolver 参数以及特定于速率限制器的参数(本节稍后将进行描述)。

keyResolver 是一个实现了 KeyResolver 接口的 bean。在配置中,使用 SpEL 通过名称引用该 bean。#{@myKeyResolver} 是一个 SpEL 表达式,它引用了一个名为 myKeyResolver 的 bean。以下清单展示了 KeyResolver 接口:

public interface KeyResolver {
Mono<String> resolve(ServerWebExchange exchange);
}

KeyResolver 接口允许通过可插拔策略来派生限流请求的密钥。在未来的里程碑版本中,将会提供一些 KeyResolver 实现。

KeyResolver 的默认实现是 PrincipalNameKeyResolver,它从 ServerWebExchange 中获取 Principal 并调用 Principal.getName()。

默认情况下,如果 KeyResolver 未找到密钥,请求将被拒绝。您可以通过设置 spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key(true 或 false)和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code 属性来调整此行为。

以下示例在 Java 中配置了一个 KeyResolver:

@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
备注

RequestRateLimiter 无法使用 "快捷" 表示法进行配置。下面的示例是 无效的:

# 无效的快捷配置
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}

以下是一个有效的 yaml 参考配置:

spring:
cloud:
gateway:
routes:
- id: limit
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
key-resolver: "#{@userkeyresolver}"

Redis RateLimiter​

Redis 实现基于 Stripe 完成的工作。它需要使用 spring-boot-starter-data-redis-reactive Spring Boot starter。

所使用的算法是 令牌桶算法。

redis-rate-limiter.replenishRate 属性定义了每秒允许的请求数(不丢弃任何请求)。这是令牌桶的填充速率。

redis-rate-limiter.burstCapacity 属性是允许用户在单秒内发出的最大请求数(不会丢弃任何请求)。这是令牌桶可以容纳的令牌数量。将此值设置为零将阻止所有请求。

redis-rate-limiter.requestedTokens 属性表示每个请求消耗的令牌数量。这是每次请求从令牌桶中取出的令牌数,默认值为 1。

通过将 replenishRate 和 burstCapacity 设置为相同的值,可以实现稳定的速率限制。若将 burstCapacity 设置为高于 replenishRate,则允许临时突发流量。在这种情况下,速率限制器需要在两次突发之间留出一定时间(根据 replenishRate),因为连续两次突发将导致请求被丢弃(HTTP 429 - Too Many Requests)。以下配置示例展示了 redis-rate-limiter 的设置:

低于 1 请求/秒 的速率限制可通过以下方式实现:将 replenishRate 设置为期望的请求数量,requestedTokens 设置为时间跨度(以秒为单位),并将 burstCapacity 设置为 replenishRate 与 requestedTokens 的乘积。例如,设置 replenishRate=1、requestedTokens=60 和 burstCapacity=60 将实现 1 请求/分钟 的限制。

spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1

这定义了每个用户的请求速率限制为每秒10次。允许突发20个请求,但在下一秒,只有10个请求可用。KeyResolver 是一个简单的解析器,用于获取 user 请求参数。注意:不建议在生产环境中使用此配置。

Bucket4j RateLimiter​

该实现基于 Bucket4j Java 库。它需要使用 com.bucket4j:bucket4j_jdk17-core 依赖项以及一个 分布式持久化选项。

在这个示例中,我们将使用 Caffeine 集成,这是一个本地缓存。你可以通过在依赖管理中引入 com.github.ben-manes.caffeine:caffeine 工件来添加它。同时,也需要导入 com.bucket4j:bucket4j_jdk17-caffeine 工件。

<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>${caffeine.version}</version>
</dependency>
<dependency>
<groupId>com.bucket4j</groupId>
<artifactId>bucket4j_jdk17-caffeine</artifactId>
<version>${bucket4j.version}</version>
</dependency>

首先,需要创建一个类型为 io.github.bucket4j.distributed.proxy.AsyncProxyMananger<String> 的 bean。

@Bean
AsyncProxyManager<String> caffeineProxyManager() {
Caffeine<String, RemoteBucketState> builder = (Caffeine) Caffeine.newBuilder().maximumSize(100);
return new CaffeineProxyManager<>(builder, Duration.ofMinutes(1)).asAsync();
}

bucket4j-rate-limiter.capacity 属性是用户在单秒内允许的最大请求数(不包含任何被丢弃的请求)。这是令牌桶能够容纳的令牌数量。该值必须大于零。

bucket4j-rate-limiter.refillPeriod 属性定义了令牌桶的补充周期。令牌桶会以每 refillPeriod 补充 refillTokens 个令牌的速率进行补充。这是一个必需属性,使用 Spring Boot Period 格式。

bucket4j-rate-limiter.refillTokens 属性定义了在 refillPeriod 期间向令牌桶中添加的令牌数量。该属性默认值为 capacity,且必须大于或等于零。

bucket4j-rate-limiter.requestedTokens 属性表示每个请求消耗的令牌数量。这是每次请求从令牌桶中取出的令牌数,默认值为 1。该值必须大于零。

bucket4j-rate-limiter.refillStyle 属性定义了令牌桶的填充方式。共有 3 种选项:GREEDY(默认)、INTERVALLY 和 INTERVALLY_ALIGNED。GREEDY 会尽可能快地将令牌添加到桶中。INTERVALLY 与贪婪模式相反,它会等待整个 refillPeriod 过去后才重新填充令牌。INTERVALLY_ALIGNED 类似于 INTERVALLY,但会指定一个 timeOfFirstRefill。

bucket4j-rate-limiter.timeOfFirstRefill 属性是一个 Instant 类型,仅在 refillStyle 设置为 INTERVALLY_ALIGNED 时使用。

以下示例定义了每个用户每秒 10 个请求的速率限制。允许突发 20 个请求,但在下一秒,只有 10 个请求可用。注意:不建议在生产环境中使用此配置

spring:
cloud:
gateway:
routes:
- id: requestratelimiter_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

自定义 RateLimiter​

你也可以将速率限制器定义为一个实现了 RateLimiter 接口的 bean。在配置中,你可以使用 SpEL 通过名称引用该 bean。#{@myRateLimiter} 是一个 SpEL 表达式,它引用了一个名为 myRateLimiter 的 bean。下面的清单定义了一个使用前一个清单中定义的 KeyResolver 的速率限制器:

spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@myRateLimiter}"
key-resolver: "#{@userKeyResolver}"