速率限制器過濾器

速率限制器過濾器使用 Bucket4j 來判斷目前請求是否允許繼續進行。如果否,則會傳回 HTTP 429 - Too Many Requests (預設) 狀態。

在閱讀此文件之前,請先檢閱 Bucket4j 概念

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

此過濾器接受 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

配置 Bucket

預設情況下,Bucket 使用已配置的 capacityperiod 進行配置。Capacity 是 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 請求中取得 principle name。