Relay is a high-performance, distributed rate limiter built with Java 21 and Spring Boot 3. It uses Redis as a centralized state store to manage request counters across multiple application instances, making it suitable for microservices and distributed architectures.
- Distributed Design: Stateless service instances share state via Redis.
- Policy-Based Configuration: Define rate limits based on URL patterns or keys.
- Rate Limiting Algorithm: Fixed window counter using Redis atomic operations (sliding-window–like behavior).
- High Performance: Minimal overhead using Redis atomic increment and TTL operations.
- Easy Deployment: Docker Compose support for instant setup.
- Java 21
- Spring Boot 3.5 (Web, Data Redis)
- Redis 7
- Docker & Docker Compose
- Maven
- Docker Desktop installed.
- (Optional) Java 21 SDK if running without Docker.
This project uses Nix Flakes to provide a reproducible development environment with Java 21, Maven, and Redis pre-configured.
- Install Nix: Download Nix
- Windows Users: You must use WSL2 to run Nix. It does not work in PowerShell/CMD directly.
- Enable Flakes: Ensure
experimental-features = nix-command flakesis in yournix.conf. - Enter Environment:
You now have
nix develop # Or if you use direnv: direnv allowjava,mvn, andredis-serveravailable in your shell, exactly matching the project version.
This is the fastest way to get everything running (Relay Service + Redis).
-
Build the application:
./mvnw clean package -DskipTests
(Note: The Dockerfile expects the jar to be in
target/) -
Start the services:
docker-compose up --build
-
The application will start on port
8080and Redis on port6379.
-
Start Redis: You need a running Redis instance on
localhost:6379.docker run -p 6379:6379 redis:7
-
Run the Application:
./mvnw spring-boot:run
Rate limiting policies are configured in src/main/resources/application.yml.
The system matches requests using a simple substring match on the "key" (usually a URL path or user ID).
ratelimiter:
policies:
# Allow 5 requests every 3 minutes for login
- pattern: "/login"
limit: 5
windowSeconds: 180
# Allow 100 requests every minute for search
- pattern: "/search"
limit: 100
windowSeconds: 60
# Default policy if no pattern matches
default:
limit: 50
windowSeconds: 60Endpoint: POST /check
Check if a specific key is allowed.
Request Body:
{
"key": "/search/user/123"
}Response:
{
"allowed": true,
"remaining": 99
}curl -X POST http://localhost:8080/check \
-H "Content-Type: application/json" \
-d '{"key": "/search"}'If the limit is exceeded:
{
"allowed": false,
"remaining": 0
}Run unit and integration tests with Maven:
./mvnw test