Backend Development 7 min read

Mastering API Rate Limiting in Spring Boot 3: Guava, Gateway, and Redis

This guide explains why API rate limiting is essential, outlines common algorithms, and walks through three practical implementations in Spring Boot 3—using Guava, Spring Cloud Gateway with RedisRateLimiter, and a custom annotation‑based solution with Redis backing.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering API Rate Limiting in Spring Boot 3: Guava, Gateway, and Redis

Overview

API rate limiting restricts access to endpoints to protect a system from overload, DDoS attacks, or malicious traffic, while ensuring stability and reliability for legitimate users.

Common limiting techniques include:

Counter – track request count per endpoint and block when a threshold is exceeded.

Rate limit – limit requests per second (or other time unit).

Sliding window – count requests within a moving time window.

Token bucket – allow a fixed number of requests in a given period regardless of burstiness.

Flow‑based – limit traffic based on network flow, such as per‑IP bandwidth.

Proper configuration balances protection with user experience.

Implementation Options

1. Guava RateLimiter

<code>@Test
public void testWithRateLimiter() {
  long start = System.currentTimeMillis();
  // Allow up to 10 requests per second
  RateLimiter limiter = RateLimiter.create(10.0);
  for (int i = 0; i < 10; i++) {
    // Blocks if no permit is available
    limiter.acquire();
    System.out.println("execution business invoke...");
    TimeUnit.SECONDS.sleep(1);
  }
  long end = System.currentTimeMillis();
  System.out.println((end - start) + "ms");
}
</code>

2. Spring Cloud Gateway with RedisRateLimiter

Spring Cloud Gateway provides the RequestRateLimiterGatewayFilterFactory filter, which uses RedisRateLimiter by default. You can customize the RateLimiter implementation as needed.

<code>spring:
  cloud:
    gateway:
      routes:
        - id: test
          uri: http://localhost:8082
          filters:
            - name: RequestRateLimiter
              args:
                key-resolver: '#{@packKeyResolver}'
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 3
</code>

3. Resilience4j

Resilience4j also offers rate‑limiting capabilities that can be applied programmatically or via annotations. Refer to the official Resilience4j documentation for details.

Custom Annotation‑Based Rate Limiting (Core Solution)

Annotation Definition

<code>@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
    // Time window in seconds
    long seconds() default 1;
    // Maximum number of calls within the window
    int count() default 10;
}
</code>

Interceptor

<code>@Component
public class AccessLimitInterceptor implements HandlerInterceptor {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod handlerMethod) {
            Method method = handlerMethod.getMethod();
            AccessLimit accessLimit = method.getDeclaredAnnotation(AccessLimit.class);
            if (accessLimit != null) {
                long seconds = accessLimit.seconds();
                int count = accessLimit.count();
                if (seconds > 0 && count >= 0) {
                    String key = request.getRemoteAddr() + ":" + request.getRequestURI();
                    String value = stringRedisTemplate.opsForValue().get(key);
                    System.out.println("Current value: " + value);
                    if (value == null) {
                        stringRedisTemplate.opsForValue().set(key, String.valueOf(count - 1), seconds, TimeUnit.SECONDS);
                        return true;
                    } else {
                        int c = Integer.valueOf(value);
                        if (c <= 0) {
                            response.setContentType("application/json;charset=utf-8");
                            Map<String, Object> res = Map.of(
                                "code", -1,
                                "message", "Too many requests"
                            );
                            response.getWriter().println(new ObjectMapper().writeValueAsString(res));
                            return false;
                        } else {
                            stringRedisTemplate.opsForValue().decrement(key);
                            return true;
                        }
                    }
                }
            }
        }
        return true;
    }
}
</code>

Interceptor Registration

<code>@Component
public class AccessLimitConfig implements WebMvcConfigurer {

    @Resource
    private AccessLimitInterceptor accessLimitInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(accessLimitInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/logout");
    }
}
</code>

Controller Example

<code>@RestController
@RequestMapping("/acc")
public class AccessLimitController {

    @AccessLimit(seconds = 1, count = 2)
    @GetMapping("/index")
    public Object index() {
        return "success";
    }
}
</code>

Testing

When accessing the endpoint, normal requests succeed. If more than two requests occur within one second, the response returns an error message indicating the request rate is too high.

JavaRedisSpring BootGuavaSpring Cloud GatewayAPI Rate Limiting
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.