Architecture
The library follows a modular architecture with clear separation of concerns:Core components
The library is organized into several key packages, each handling a specific responsibility:Core interfaces
Thecore package defines the fundamental abstractions:
RateLimiter
Evaluates whether a request should be allowed based on a key and policy
RateLimitEnforcer
Orchestrates the rate limiting flow and throws exceptions when limits are exceeded
RateLimitContext
Provides invocation context (method, arguments, target class) to resolvers
RateLimitPolicyProvider
Resolves rate limit policies from annotations or external configuration
Redis implementation
Theredis package contains the RedisRateLimiter class (redis/RedisRateLimiter.java:19), which implements a fixed-window rate limiting algorithm using Redis atomic operations:
- Uses
INCRto atomically increment request counters - Sets
TTLto automatically expire window buckets - Calculates window boundaries based on current time
- Supports both fail-open and fail-closed behavior
The fixed-window algorithm divides time into discrete windows. Each window starts at a deterministic timestamp calculated from the current time and window duration.
Key resolution
Thekey package provides strategies for determining which Redis key (bucket) to charge:
- RateLimitKeyResolver: Interface for custom key resolution strategies
- DefaultRateLimitKeyResolver (key/DefaultRateLimitKeyResolver.java:11): Default implementation that combines scope, class name, and method name
Configuration
Theconfig package handles Spring Boot integration:
- RateLimiterProperties (config/RateLimiterProperties.java:9): Externalized configuration via
application.properties - RateLimiterAutoConfiguration: Auto-configures all beans when Redis is available
How it works
When you annotate a method with@RateLimit, here’s what happens:
Key resolution
The
RateLimitKeyResolver generates a unique key (e.g., global:com.example.ApiController#getData)Policy resolution
The
RateLimitPolicyProvider extracts the policy from the annotation (limit, duration, scope)Rate limit evaluation
The
RedisRateLimiter checks if the request should be allowed:- Calculates the current window boundary
- Increments the Redis counter for this window
- Compares count against the limit
Request lifecycle example
Here’s a complete example of how a rate-limited request flows through the system:Step 1: Interception
Step 1: Interception
The AOP aspect intercepts the call before the method body executes.
Step 2: Key generation
Step 2: Key generation
The The
DefaultRateLimitKeyResolver generates a key like:user prefix comes from the scope attribute.Step 3: Window calculation
Step 3: Window calculation
For a 60-second window at timestamp 1,678,900,825,000 ms:This ensures all requests in the same minute use the same window.
Step 4: Redis operation
Step 4: Redis operation
The limiter executes:The TTL is set to 61 seconds (60 + 1 second safety buffer).
Step 5: Decision
Step 5: Decision
If the counter is ≤ 10, the request is allowed. Otherwise, a
RateLimitExceededException is thrown with retry information.Design principles
The library is built on several key principles:Framework-agnostic core
Core interfaces don’t depend on Spring Web, making them usable in any context (servlets, reactive, gRPC)
Extensibility
You can provide custom implementations of
RateLimitKeyResolver, RateLimitPolicyProvider, or even RateLimiterObservability
Built-in Micrometer metrics integration for monitoring rate limit decisions
Fault tolerance
Configurable fail-open behavior ensures your application stays available even if Redis is down
The library uses Spring Boot’s auto-configuration, so you only need to add the dependency and configure Redis. All beans are created automatically.
Next steps
Rate limiting algorithm
Deep dive into the fixed-window algorithm and Redis operations
Key resolution
Learn how keys are generated and how to customize resolution strategies
Fail-open vs fail-closed
Understand fault tolerance strategies when Redis is unavailable