限流器过滤器

RateLimiter 过滤器使用 Bucket4j 来确定当前请求是否允许继续处理。如果请求不被允许,(默认情况下)将返回状态码 HTTP 429 - Too Many Requests

在阅读本文档之前,请先回顾 Bucket4j 概念

Bucket4j 使用的算法是 令牌桶算法

该过滤器接受一个 keyResolver 参数和其他 Bucket4j 配置参数。键解析器是一个 java.util.Function<ServerRequest, String>。这允许用户从请求中提取任何信息,以便在配置的 Bucket4j 分布式 机制中用作键。一个常见的键可以是根据 ServerRequest 获取的 Principal

默认情况下,如果键解析器找不到键,请求将被拒绝并返回 FORBIDDEN 状态码。

目前,配置键解析器唯一的方法是通过 Java DSL,而不是通过外部属性。

Bucket4j 分布式配置

一个类型为 io.github.bucket4j.distributed.proxy.AsyncProxyManager 的 bean。为此,请使用 ProxyManager.asAsync() 方法。

RateLimiterConfiguration.java
import com.github.benmanes.caffeine.cache.Caffeine;
import io.github.bucket4j.caffeine.CaffeineProxyManager;

@Configuration
class RateLimiterConfiguration {

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

上述配置使用 Caffeine(一个本地内存缓存,适用于测试)来设置一个 AsyncProxyManager

配置令牌桶

默认情况下,令牌桶使用配置的 capacityperiod 进行配置。Capacity 是指令牌桶拥有的令牌数量。Period 是一个 java.util.Duration,它定义了令牌桶中可用令牌再生所需的时间。

其他配置项包括请求被拒绝时返回的 statusCode。默认值为 429,即 TOO_MANY_REQUESTS。tokens 项定义了每个请求使用的令牌数量,默认为 1。headerName 项是包含剩余令牌数量的响应头的名称,默认值为 X-RateLimit-Remainingtimeout 选项定义了分布式令牌桶返回结果的 Duration,默认未设置。

以下是配置带有限流功能的路由的示例

RouteConfiguration.java
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.filter.Bucket4jFilterFunctions.rateLimit;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;

@Configuration
class RouteConfiguration {

    @Bean
    public RouterFunction<ServerResponse> gatewayRouterFunctionsRateLimited() {
        return route("rate_limited_route")
            .GET("/api/**", http())
            .before(uri("https://example.org"))
            .filter(rateLimit(c -> c.setCapacity(100)
                    .setPeriod(Duration.ofMinutes(1))
                    .setKeyResolver(request -> request.servletRequest().getUserPrincipal().getName())))
            .build();
    }
}

这配置了每分钟 100 个令牌的令牌桶容量来进行限流。键解析器从 Servlet 请求中获取主体名称。