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 接口定义:
public interface KeyResolver {
Mono<String> resolve(ServerWebExchange exchange);
}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 属性来调整此行为。
自定义 KeyResolver 示例 (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 配置:
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。
配置示例:
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 依赖:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>com.bucket4j</groupId>
<artifactId>bucket4j_jdk17-caffeine</artifactId>
</dependency>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 配置示例:
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 配置示例:
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 路径 -> 针对接口限流(防止某个接口被刷爆)。