CDI managed bean for rate limiting requests per client identifier. This utility provides a thread-safe sliding window rate limiting mechanism with configurable request limits and time windows.
The rate limiter supports both HTTP request-based rate limiting (using client IP addresses) and custom
client identifier-based rate limiting (using any string identifier such as user IDs, API keys, etc.).
It uses a ConcurrentHashMap to track request counts per client identifier and automatically
cleans up expired entries to prevent memory leaks.
When the rate limit is exceeded, the rate limiter will by default immediately throw a RateLimitExceededException.
Optionally, you can configure automatic retries via the maxRetries parameter, which will retry the request
after a calculated delay based on the remaining time window. If all retries are exhausted, a
RateLimitExceededException is thrown.
Usage
The recommended usage is with the RateLimit annotation.
@Named
@RequestScoped
public class ApiController {
@RateLimit(clientId = "FooAPI", maxRequestsPerTimeWindow = 10, maxRetries = 3)
public void processFooApiRequest() {
// Process Foo API request ...
}
}
For more fine grained usage, or when you have a variable rate limit parameter which therefore cannot be set as an
annotation attribute, then you can inject this CDI bean in any CDI managed artifact and explicitly invoke either
checkRateLimit(HttpServletRequest, int, Duration, int) or checkRateLimit(String, int, Duration, int).
Below is an example of a servlet filter that applies rate limiting to all requests depending on client IP address:
@WebFilter("/*")
public class RateLimitFilter extends HttpFilter {
@Inject
private RateLimiter rateLimiter;
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
var maxRequestsPerTimeWindow = determineMaxRequestsBasedOn(request);
var timeWindowInSeconds = determineTimeWindowBasedOn(request);
var maxRetries = 0;
try {
rateLimiter.checkRateLimit(request, maxRequestsPerTimeWindow, Duration.ofSeconds(timeWindowInSeconds), maxRetries);
chain.doFilter(request, response);
}
catch (RateLimitExceededException e) {
response.sendError(429); // Too Many Requests
}
}
}
- Since:
- 5.0
- Author:
- Bauke Scholtz
- See Also:
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidcheckRateLimit(HttpServletRequest request, int maxRequestsPerTimeWindow, Duration timeWindow, int maxRetries) Checks if the current request exceeds the configured rate limit for the client IP address associated with the given request.voidcheckRateLimit(String clientId, int maxRequestsPerTimeWindow, Duration timeWindow, int maxRetries) Checks if the current request exceeds the configured rate limit for the given client identifier.voiddestroy()If the scheduled executor service was created with help ofExecutors.newSingleThreadExecutor(), then attempt to orderly shut down it.voidinit()First looks up the JEE default managed scheduled executor service in JNDI.
-
Constructor Details
-
RateLimiter
public RateLimiter()
-
-
Method Details
-
init
First looks up the JEE default managed scheduled executor service in JNDI. If it isn't available, then create scheduled executor service ourselves with help ofExecutors.newSingleThreadExecutor(). -
checkRateLimit
public void checkRateLimit(HttpServletRequest request, int maxRequestsPerTimeWindow, Duration timeWindow, int maxRetries) throws RateLimitExceededException Checks if the current request exceeds the configured rate limit for the client IP address associated with the given request.This method implements a sliding window rate limiting algorithm. It tracks the number of requests per IP address within the specified time window and throws an exception if the limit is exceeded. Expired tracking entries are automatically cleaned up to prevent memory leaks.
- Parameters:
request- The HTTP servlet request to check.maxRequestsPerTimeWindow- The maximum number of requests allowed within the time window.timeWindow- The time window duration.maxRetries- The maximum number of retries.- Throws:
RateLimitExceededException- When the rate limit is exceeded for the client IP address associated with the given request.
-
checkRateLimit
public void checkRateLimit(String clientId, int maxRequestsPerTimeWindow, Duration timeWindow, int maxRetries) throws RateLimitExceededException Checks if the current request exceeds the configured rate limit for the given client identifier.This method implements a sliding window rate limiting algorithm. It tracks the number of requests per client identifier within the specified time window and throws an exception if the limit is exceeded. Expired tracking entries are automatically cleaned up to prevent memory leaks.
- Parameters:
clientId- The client identifier to check, whether client IP, user ID, API key, etc.maxRequestsPerTimeWindow- The maximum number of requests allowed within the time window.timeWindow- The time window duration.maxRetries- The maximum number of retries.- Throws:
RateLimitExceededException- When the rate limit is exceeded for the given client identifier.
-
destroy
If the scheduled executor service was created with help ofExecutors.newSingleThreadExecutor(), then attempt to orderly shut down it. If it's still not shut down after 5 seconds, then terminate it.
-