VOOZH about

URL: https://www.javacodegeeks.com/2025/05/rate-limiting-in-java-implementing-per-user-throttling-with-redis-buckets.html

⇱ Rate Limiting in Java: Implementing Per-User Throttling with Redis Buckets - Java Code Geeks


To protect APIs from abuse and ensure fair usage, rate limiting is essential. It prevents users or clients from overwhelming the system by enforcing a limit on how often they can call your APIs. In this guide, you’ll learn how to implement per-user throttling in Java using Spring Boot and Redis, using the token bucket algorithm.

1. Why Use Redis for Rate Limiting?

  • Low latency and high throughput
  • Atomic operations using Redis scripts (Lua)
  • Persistence (optional) and cluster support
  • Suitable for distributed systems — state doesn’t live in application memory

2. The Token Bucket Algorithm

The token bucket algorithm is widely used for rate limiting. Here’s how it works:

  • Each user has a bucket with tokens.
  • Each request consumes one token.
  • Tokens are refilled at a fixed rate.
  • If there are no tokens, the request is denied or delayed.

This model allows for short bursts while maintaining an average rate.

3. Redis Lua Script for Rate Limiting

-- token_bucket.lua
local key = KEYS[1]
local max_tokens = tonumber(ARGV[1])
local refill_rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])

local bucket = redis.call("HMGET", key, "tokens", "timestamp")
local tokens = tonumber(bucket[1]) or max_tokens
local timestamp = tonumber(bucket[2]) or now

local delta = math.max(0, now - timestamp)
tokens = math.min(max_tokens, tokens + delta * refill_rate)
local allowed = tokens >= requested
if allowed then
 tokens = tokens - requested
end

redis.call("HMSET", key, "tokens", tokens, "timestamp", now)
redis.call("EXPIRE", key, 60)

return allowed

4. Spring Boot Integration

1. Redis Configuration

@Configuration
public class RedisConfig {
 @Bean
 public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
 RedisTemplate<String, String> template = new RedisTemplate<>();
 template.setConnectionFactory(factory);
 return template;
 }
}

2. RateLimiter Service

@Service
public class RateLimiterService {

 @Autowired
 private RedisTemplate<String, String> redisTemplate;

 private final DefaultRedisScript<Boolean> rateLimiterScript;

 public RateLimiterService() {
 rateLimiterScript = new DefaultRedisScript<>();
 rateLimiterScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("token_bucket.lua")));
 rateLimiterScript.setResultType(Boolean.class);
 }

 public boolean isAllowed(String userId) {
 String key = "bucket:" + userId;
 List<String> keys = List.of(key);

 Long now = Instant.now().getEpochSecond();
 return Boolean.TRUE.equals(redisTemplate.execute(
 rateLimiterScript,
 keys,
 "10", // max_tokens
 "1", // refill_rate (tokens/sec)
 now.toString(),
 "1" // requested tokens
 ));
 }
}

3. Middleware/Interceptor

@Component
public class RateLimitingInterceptor implements HandlerInterceptor {

 @Autowired
 private RateLimiterService rateLimiterService;

 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
 String userId = request.getHeader("X-User-Id");
 if (userId == null || !rateLimiterService.isAllowed(userId)) {
 response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
 return false;
 }
 return true;
 }
}

4. Register the Interceptor

@Configuration
public class WebConfig implements WebMvcConfigurer {
 @Autowired
 private RateLimitingInterceptor rateLimitingInterceptor;

 @Override
 public void addInterceptors(InterceptorRegistry registry) {
 registry.addInterceptor(rateLimitingInterceptor);
 }
}

5. Benefits of This Approach

  • ⚙️ Scalable: Stateless, works in distributed environments
  • 🧪 Testable: Token bucket logic lives in Redis script
  • 🔐 Secure: Per-user rate limiting avoids abuse
  • 🕰️ Flexible: Adjust tokens, refill rates per endpoint/user type

6. Optional Enhancements

  • Use a different refill rate per API key or user tier
  • Implement burst handling via leaky bucket fallback
  • Store limits in Redis hashes or config maps
  • Expose remaining quota in HTTP headers

7. Conclusion

Rate limiting is critical for building resilient APIs. By combining Spring Boot, Redis, and a token bucket algorithm, you get a robust and scalable per-user rate limiter that supports zero-downtime, distributed environments. Redis handles concurrency and token math, while your app stays clean and fast.

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Thank you!

We will contact you soon.

👁 Photo of Eleftheria Drosopoulou
Eleftheria Drosopoulou
May 23rd, 2025Last Updated: May 18th, 2025
0 613 2 minutes read

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button
Close
wpDiscuz