Skip to content

RequestRateLimiter GatewayFilter 工厂

RequestRateLimiter GatewayFilter 工厂使用 RateLimiter 实现来确定当前请求是否允许继续。如果请求被拒绝,默认返回状态码 HTTP 429 - Too Many Requests

此过滤器接受一个可选的 keyResolver 参数以及特定于速率限制器的参数。

Key Resolver (键解析器)

keyResolver 是一个实现了 KeyResolver 接口的 Bean。 在配置中,使用 SpEL 表达式按名称引用该 Bean。例如,#{@myKeyResolver} 是一个引用名为 myKeyResolver 的 Bean 的 SpEL 表达式。

KeyResolver 接口允许通过可插拔的策略来推导用于限制请求的键(Key)。

KeyResolver 接口定义:

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

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

默认情况下,如果 KeyResolver 没有找到键,请求将被拒绝。你可以通过设置 spring.cloud.gateway.filter.request-rate-limiter.deny-empty-keytruefalse)和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code 属性来调整此行为。

自定义 KeyResolver 示例 (Java):

java
@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 配置:

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。

使用的算法是 令牌桶算法 (Token Bucket Algorithm)

  • redis-rate-limiter.replenishRate: 定义每秒允许多少个请求(没有任何丢弃的请求)。这是令牌桶被填充的速率。
  • redis-rate-limiter.burstCapacity: 用户在单秒内允许的最大请求数(没有任何丢弃的请求)。这是令牌桶可以容纳的令牌数量。将此值设置为零将阻止所有请求。
  • redis-rate-limiter.requestedTokens: 一个请求消耗多少个令牌。默认为 1

配置示例:

yaml
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 个请求可用。

Bucket4j RateLimiter

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

Maven 依赖:

xml
<dependency>
  <groupId>com.github.ben-manes.caffeine</groupId>
  <artifactId>caffeine</artifactId>
</dependency>
<dependency>
  <groupId>com.bucket4j</groupId>
  <artifactId>bucket4j_jdk17-caffeine</artifactId>
</dependency>

Java 配置:

java
@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: 定义回填周期。这是一个必需的属性,使用 Spring Boot Duration 格式(如 1s, 1m)。
  • bucket4j-rate-limiter.refillTokens: 在 refillPeriod 期间添加到桶中的令牌数。默认为 capacity
  • bucket4j-rate-limiter.requestedTokens: 一个请求消耗的令牌数。默认为 1
  • bucket4j-rate-limiter.refillStyle: 定义回填方式。选项有 GREEDY(默认,贪婪填充)、INTERVALLY(间隔填充)和 INTERVALLY_ALIGNED(对齐间隔填充)。

YAML 配置示例:

yaml
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。

YAML 配置示例:

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

补充教学 —— 限流的实战应用

1. 为什么要限流? 保护后端服务不被瞬间的高并发流量冲垮(比如秒杀活动、恶意爬虫、DDoS 攻击)。

2. 令牌桶算法 (Token Bucket) 通俗解释

  • 想象一个水桶(Bucket)。
  • 有一个水龙头以固定的速度往里面滴水(replenishRate,比如每秒滴10滴)。
  • 水桶满了水就会溢出,桶的大小就是 burstCapacity(比如20滴)。
  • 每个请求过来,都要从桶里舀一勺水(requestedTokens,默认1滴)。
  • 如果桶里有水,请求通过;如果桶空了,请求被拒绝(HTTP 429)。
  • 突发流量:因为桶里可以存水,所以允许短时间内来一大波请求(只要不超过桶的容量),这就是 burstCapacity 的作用。

3. KeyResolver 的作用

  • 限流对象:你是想针对所有用户限流,还是针对每个 IP 限流,还是针对每个用户 ID 限流?
  • KeyResolver 就是用来提取这个“限流对象”的 ID 的。
    • 返回 IP -> 针对 IP 限流(防止某个 IP 刷接口)。
    • 返回 UserID -> 针对用户限流(防止某个用户刷接口)。
    • 返回 API 路径 -> 针对接口限流(防止某个接口被刷爆)。

Based on Spring Framework.