RateLimiter Filter

RateLimiter Filter 使用 Bucket4j 确定是允许当前请求继续进行。如果不是,则返回状态 HTTP 429 - Too Many Requests(默认)。 在阅读本文档之前,请先查看 Bucket4j Concepts。 Bucket4j 使用的算法是 Token Bucket Algorithm。 该过滤器采用 keyResolver 参数和其他 Bucket4j 配置参数。键解析器是 java.util.Function<ServerRequest, String>。这使用户能够从请求中提取任何信息,以用作在已配置的 Bucket4j distribution 机制中作为键。一个常见的键将是从 ServerRequest 检索到的 Principal。 默认情况下,如果关键解析器找不到键,那么将会以 FORBIDDEN 状态拒绝请求。

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

Bucket4j Distributed Configuration

一个 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,对于测试非常有用。

Configuring Buckets

默认情况下,Bucket 会使用已配置的 “capacity” 和 “period” 进行配置。容量是指 Bucket 中有多少个令牌。period 是一个 java.util.Duration,它定义了 Bucket 中可用的令牌重新生成的时长。

其他配置项是请求被拒绝时返回的 statusCode。默认情况下它为 429,即 TO_MANY_REQUESTS。tokens 项定义每个请求使用了多少令牌,默认值为 1。headerName 项是包含剩余令牌数的标头的名称,默认为 X-RateLimit-Remainingtimeout 选项为分布式 Bucket 返回答案定义一个 Duration,默认情况下不会设置。

下面是一个为路由配置限流的示例:

RouteConfiguration.java
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("https://example.org"))
				.filter(rateLimit(c -> c.setCapacity(100)
					.setPeriod(Duration.ofMinutes(1))
					.setKeyResolver(request -> request.servletRequest().getUserPrincipal().getName())))
				.build();
    }
}

这将使用每分钟 100 个令牌的 Bucket 容量来配置限流。密钥解析器从 Servlet 请求中获取主体名称。