速率限制器過濾器
速率限制器過濾器使用 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()
方法。
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 使用已配置的 capacity
和 period
進行配置。Capacity 是 bucket 擁有的令牌數量。Period 是一個 java.util.Duration
,用於定義 bucket 中可用的令牌重新產生的時間長度。
其他組態項目包括請求被拒絕時傳回的 statusCode
。預設值為 429, TO_MANY_REQUESTS。tokens
項目定義每個請求使用的令牌數量,預設值為 1。headerName
項目是包含剩餘令牌數量的標頭名稱,預設值為 X-RateLimit-Remaining
。timeout
選項定義分散式 bucket 傳回答案的 Duration
,預設情況下未設定。
以下是以速率限制配置路由的範例
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。