环境:SpringBoot2.4.13 + Spring Cloud Gateway3.0.1
概述
RequestRateLimiter GatewayFilter工厂使用一个RateLimiter实现来确定当前请求是否允许继续。如果不是,返回HTTP 429 - Too Many Requests(默认情况下)的状态。
该过滤器接受一个可选的keyResolver参数和特定于速率限制器的参数。该参数的作用就是用来根据你设定的规则生成key,比如在redis中使用什么key。
keyResolver是一个实现KeyResolver接口的bean。在配置中,使用SpEL按名称引用bean。#{@userKeyResolver}是一个SpEL表达式,它引用了一个名为userKeyResolver的bean。KeyResolver接口如下所示:
public interface KeyResolver {
Mono<String> resolve(ServerWebExchange exchange);
}
默认情况下,如果KeyResolver没有找到key,请求将被拒绝。你可以通过设置
spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true或false)和spring.cloud.gateway.filter.request-rate- limititer来调整这种行为。empty-key-status-code属性。
注意:RequestRateLimiter不能用“快捷方式”表示法进行配置。以下示例无效:
# INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}
使用Redis限流
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)。
低于1个请求/秒的速率限制是通过以下方式实现的:将replenishRate设置为所需的请求数量,将requestedTokens设置为以秒为单位的时间跨度,将burstCapacity设置为replenishRate和requestedToken的乘积,例如,将replenishRate设为1,requestedTokens=60, burstCapability设置为60,将导致1 request/min的限制。
配置示例:
spring:
cloud:
gateway:
default-filters:
- StripPrefix=1
routes:
- id: o001
uri: lb://order-service
predicates:
- Path=/api-a/**, /api-b/**
filters:
- name: RequestRateLimiter
args:
#每秒允许用户执行的请求数,而不丢弃任何请求。这是令牌桶的填充速率。
redis-rate-limiter.replenishRate: 1
#允许用户在一秒钟内完成的最大请求数。这是令牌桶可以容纳的令牌数。将此值设置为零将阻止所有请求。
redis-rate-limiter.burstCapacity: 2
#一个请求需要多少令牌。这是每个请求从存储桶中获取的令牌数,默认为1。
redis-rate-limiter.requestedTokens: 1
keyResolver: "#{@userKeyResolver}"
#自定义状态码
#statusCode: INTERNAL_SERVER_ERROR
@Configuration
public class RedisRateConfig {
@Bean
public KeyResolver userKeyResolver() {
// 以orderId限流
// return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("orderId"));
// 以ip限流
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()) ;
}
}
解释:
这定义了每个IP1个请求速率的限制。允许2个请求的突发,但是在下一秒,只有1个请求可用。KeyResolver是一个获取用户请求参数的简单方法(注意,不建议在生产环境中使用)。
自定义限速器
还可以将速率限制器定义为实现RateLimiter接口的bean。在配置中,您可以使用SpEL按名称引用bean。#{@userRateLimiter}是一个SpEL表达式,它引用了一个名为userRateLimiter的bean。下面的清单定义了一个使用上一个清单中定义的KeyResolver的速率限制器:
spring:
cloud:
gateway:
default-filters:
- StripPrefix=1
routes:
- id: o001
uri: lb://order-service
predicates:
- Path=/api-a/**, /api-b/**
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@userRateLimiter}"
keyResolver: "#{@userKeyResolver}"
public class UserRateLimiter implements RateLimiter<UserRateLimiter.Config> {
@Override
public Map<String, Config> getConfig() {
return null;
}
@Override
public Class<Config> getConfigClass() {
return null;
}
@Override
public Config newConfig() {
return null;
}
@Override
public Mono<Response> isAllowed(String routeId, String id) {
return null;
}
public static class Config {
@Min(1)
private int replenishRate;
@Min(0)
private int burstCapacity = 1;
@Min(1)
private int requestedTokens = 1;
public int getReplenishRate() {
return replenishRate;
}
public Config setReplenishRate(int replenishRate) {
this.replenishRate = replenishRate;
return this;
}
public int getBurstCapacity() {
return burstCapacity;
}
public Config setBurstCapacity(int burstCapacity) {
this.burstCapacity = burstCapacity;
return this;
}
public int getRequestedTokens() {
return requestedTokens;
}
public Config setRequestedTokens(int requestedTokens) {
this.requestedTokens = requestedTokens;
return this;
}
@Override
public String toString() {
return new ToStringCreator(this).append("replenishRate", replenishRate)
.append("burstCapacity", burstCapacity).append("requestedTokens", requestedTokens).toString();
}
}
}
完毕!!!
Spring Cloud Gateway核心过滤器之请求限流详解
原创2022-11-21 08:20·Spring全家桶实战案例
环境:SpringBoot2.4.13 + Spring Cloud Gateway3.0.1
概述
RequestRateLimiter GatewayFilter工厂使用一个RateLimiter实现来确定当前请求是否允许继续。如果不是,返回HTTP 429 - Too Many Requests(默认情况下)的状态。
该过滤器接受一个可选的keyResolver参数和特定于速率限制器的参数。该参数的作用就是用来根据你设定的规则生成key,比如在redis中使用什么key。
keyResolver是一个实现KeyResolver接口的bean。在配置中,使用SpEL按名称引用bean。#{@userKeyResolver}是一个SpEL表达式,它引用了一个名为userKeyResolver的bean。KeyResolver接口如下所示:
public interface KeyResolver {
Mono<String> resolve(ServerWebExchange exchange);
}
默认情况下,如果KeyResolver没有找到key,请求将被拒绝。你可以通过设置
spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true或false)和spring.cloud.gateway.filter.request-rate- limititer来调整这种行为。empty-key-status-code属性。
注意:RequestRateLimiter不能用“快捷方式”表示法进行配置。以下示例无效:
# INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}
使用Redis限流
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)。
低于1个请求/秒的速率限制是通过以下方式实现的:将replenishRate设置为所需的请求数量,将requestedTokens设置为以秒为单位的时间跨度,将burstCapacity设置为replenishRate和requestedToken的乘积,例如,将replenishRate设为1,requestedTokens=60, burstCapability设置为60,将导致1 request/min的限制。
配置示例:
spring:
cloud:
gateway:
default-filters:
- StripPrefix=1
routes:
- id: o001
uri: lb://order-service
predicates:
- Path=/api-a/**, /api-b/**
filters:
- name: RequestRateLimiter
args:
#每秒允许用户执行的请求数,而不丢弃任何请求。这是令牌桶的填充速率。
redis-rate-limiter.replenishRate: 1
#允许用户在一秒钟内完成的最大请求数。这是令牌桶可以容纳的令牌数。将此值设置为零将阻止所有请求。
redis-rate-limiter.burstCapacity: 2
#一个请求需要多少令牌。这是每个请求从存储桶中获取的令牌数,默认为1。
redis-rate-limiter.requestedTokens: 1
keyResolver: "#{@userKeyResolver}"
#自定义状态码
#statusCode: INTERNAL_SERVER_ERROR
@Configuration
public class RedisRateConfig {
@Bean
public KeyResolver userKeyResolver() {
// 以orderId限流
// return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("orderId"));
// 以ip限流
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()) ;
}
}
解释:
这定义了每个IP1个请求速率的限制。允许2个请求的突发,但是在下一秒,只有1个请求可用。KeyResolver是一个获取用户请求参数的简单方法(注意,不建议在生产环境中使用)。
自定义限速器
还可以将速率限制器定义为实现RateLimiter接口的bean。在配置中,您可以使用SpEL按名称引用bean。#{@userRateLimiter}是一个SpEL表达式,它引用了一个名为userRateLimiter的bean。下面的清单定义了一个使用上一个清单中定义的KeyResolver的速率限制器:
spring:
cloud:
gateway:
default-filters:
- StripPrefix=1
routes:
- id: o001
uri: lb://order-service
predicates:
- Path=/api-a/**, /api-b/**
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@userRateLimiter}"
keyResolver: "#{@userKeyResolver}"
public class UserRateLimiter implements RateLimiter<UserRateLimiter.Config> {
@Override
public Map<String, Config> getConfig() {
return null;
}
@Override
public Class<Config> getConfigClass() {
return null;
}
@Override
public Config newConfig() {
return null;
}
@Override
public Mono<Response> isAllowed(String routeId, String id) {
return null;
}
public static class Config {
@Min(1)
private int replenishRate;
@Min(0)
private int burstCapacity = 1;
@Min(1)
private int requestedTokens = 1;
public int getReplenishRate() {
return replenishRate;
}
public Config setReplenishRate(int replenishRate) {
this.replenishRate = replenishRate;
return this;
}
public int getBurstCapacity() {
return burstCapacity;
}
public Config setBurstCapacity(int burstCapacity) {
this.burstCapacity = burstCapacity;
return this;
}
public int getRequestedTokens() {
return requestedTokens;
}
public Config setRequestedTokens(int requestedTokens) {
this.requestedTokens = requestedTokens;
return this;
}
@Override
public String toString() {
return new ToStringCreator(this).append("replenishRate", replenishRate)
.append("burstCapacity", burstCapacity).append("requestedTokens", requestedTokens).toString();
}
}
}
完毕!!!