Implementing a Distributed Rate Limiter with Redis, Spring Boot, and Lua Scripts
This article demonstrates how to build a distributed rate‑limiting component named shield‑ratelimiter using Redis’s INCR and TTL features, Spring‑Boot‑starter integration, Lua scripting for atomic operations, custom annotations, and AspectJ, providing a robust, configurable solution for limiting API calls in Java backend services.
In high‑traffic distributed systems, sudden spikes in request volume can overload backend services, so rate limiting, degradation, and circuit breaking are essential. This guide introduces a custom component called shield‑ratelimiter that leverages Redis’s INCR and EXPIRE capabilities to enforce a maximum number of requests per time window.
Principle : For each client (identified by IP), a Redis key is created with a configurable expiration (e.g., 1 second). Each request increments the key; when the count exceeds the configured limit, further requests are rejected. The logic is executed atomically via a Lua script.
Requirements :
Use Redis INCR and expiration for counting.
Provide a declarative, annotation‑based API.
Integrate seamlessly with Spring Boot.
Why Lua? Lua scripts run on the Redis server as a single atomic operation, eliminating network round‑trips and race conditions that would arise with separate INCR and EXPIRE calls from Java.
Implementation steps :
Create a Maven project with spring-boot-starter and spring-boot-starter-redis dependencies.
Configure Redis in a @Configuration class, injecting CacheManager and a RedisTemplate that uses Jackson2JsonRedisSerializer for values and StringRedisSerializer for keys.
Define a @RateLimiter annotation with attributes key, limit, and expire.
Implement an AspectJ component ( RateLimterHandler) that extracts annotation parameters, builds a key list, and executes the Lua script via redisTemplate.execute. The script returns 0 for blocked requests and 1 for allowed ones.
Load the Lua script ( rateLimter.lua) from the classpath using ClassPathResource and DefaultRedisScript.
The Lua script performs the following logic:
local key1 = KEYS[1]
local val = redis.call('incr', key1)
local ttl = redis.call('ttl', key1)
local expire = ARGV[1]
local times = ARGV[2]
if val == 1 then
redis.call('expire', key1, tonumber(expire))
else
if ttl == -1 then
redis.call('expire', key1, tonumber(expire))
end
end
if val > tonumber(times) then
return 0
end
return 1Testing : A sample Spring MVC controller method is annotated with @RateLimiter(key="ratedemo:1.0.0", limit=5, expire=100). Sending multiple HTTP requests shows five successful responses followed by a rate‑limit response, confirming the component works as intended.
Conclusion : By combining Redis atomic counters, Lua scripting, and Spring Boot’s annotation and AOP facilities, a lightweight yet powerful distributed rate‑limiting solution is achieved. The approach can be extended or customized for production environments, and the source code is available on GitHub.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
