Skip to content

Latest commit

 

History

History
149 lines (99 loc) · 4.31 KB

File metadata and controls

149 lines (99 loc) · 4.31 KB

API Rate Limiter

Java Spring Boot License

A Spring Boot application with rate limiting: per-IP request limits with in-memory and Redis backends, Prometheus metrics, and Grafana dashboards.

FeaturesQuick startConfigurationAPI


Stack

  • Java 21, Spring Boot 3.2
  • Redis (optional) — for a distributed rate limiter across instances
  • Micrometer + Prometheus — metrics
  • Docker / Docker Compose — run the full stack

Features

  • Fixed window algorithm: limit requests per time window
  • Two modes: in-memory (default) and Redis (profile redis)
  • Filter applies to all requests except /actuator/**
  • Limit key: client IP (request.getRemoteAddr())
  • When limit is exceeded: 429 Too Many Requests with JSON {"error":"Too many requests"}
  • Metrics: rate_limit_requests_allowed_total, rate_limit_requests_rejected_total

Requirements

  • JDK 21, Gradle (or the included wrapper)
  • For Redis mode: a running Redis instance
  • For the full stack: Docker and Docker Compose

Quick start

git clone https://github.com/NullPoint3rDev/api-rate-limiter.git
cd api-rate-limiter
docker compose up --build

Then open http://localhost:8080/api/ping and http://localhost:3001 (Grafana: admin / admin).

Running

Locally (in-memory)

./gradlew bootRun

App: http://localhost:8080. Rate limiter is in-memory, no Redis.

Locally with Redis

Start Redis (e.g. docker run -d -p 6379:6379 redis:7-alpine), then:

./gradlew bootRun --args='--spring.profiles.active=redis'

Docker (app only)

docker build -t api-rate-limiter:latest .
docker run -p 8080:8080 api-rate-limiter:latest

Uses in-memory mode. For Redis, set env vars and link to a Redis container.

Docker Compose (app + Redis + Prometheus + Grafana)

docker compose up --build
Service URL
API http://localhost:8080
Prometheus http://localhost:9091
Grafana http://localhost:3001

Grafana: login admin, password admin. Prometheus datasource is pre-configured (http://prometheus:9090).

Configuration

In application.yml (or environment variables):

Property Default Description
rate-limit.limit 3 Max requests per window
rate-limit.window-size 60s Window duration
spring.profiles.active redis for Redis rate limiter
spring.data.redis.host localhost Redis host
spring.data.redis.port 6379 Redis port

Example via command-line args:

./gradlew bootRun --args='--rate-limit.limit=10 --rate-limit.window-size=30s'

API

  • GET /api/ping — health check; returns OK and 200 when not rate-limited.
  • GET /actuator/health, /actuator/prometheus, /actuator/metrics — not rate-limited.

When the limit is exceeded, any request (except /actuator/**) returns 429 with body:

{"error":"Too many requests"}

Example: test with limit 3 per 60s:

for i in {1..6}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/api/ping; done
# Expected: 200, 200, 200, 429, 429, 429

Metrics

  • rate_limit_requests_allowed_total — allowed requests
  • rate_limit_requests_rejected_total — rejected (429)

Prometheus scrape endpoint: GET /actuator/prometheus. In Grafana, use Explore, select the Prometheus datasource, and query these metrics.

Tests

./gradlew test

Unit tests (in-memory rate limiter) run by default. Redis integration tests (Testcontainers) are skipped unless enabled:

RUN_REDIS_IT=true ./gradlew test

Docker must be running (for the Redis container).

License

MIT