Implement Rate Limiting with Spring Cloud Gateway and Redis
This tutorial explains how to restrict client access to specific APIs within a time window using Spring Cloud Gateway and Redis, covering the concept, implementation options, and complete code examples for a Redis‑backed rate limiter.
1 Concept
Through a mechanism, limit the number of times a specific client can access a particular interface within a defined time window; exceeding the limit blocks access until the period expires. Two essential elements: the client identity and the target interface.
Client identity
Target interface
2 Implementation Principle
Two approaches: via gateway (covers all services) or via AOP (applies per service, leads to code duplication).
This article implements the gateway approach. To record how many times a client has accessed an interface within the time window, two methods are possible: JVM‑level (requires custom expiration logic) or Redis (leverages key expiration).
Using Redis, each request decrements a counter; when the count falls below zero, an error is returned. Redis automatically deletes expired keys.
3 Code Implementation
Redis configuration:
spring:
redis:
host: localhost
port: 6379
password: 123123
database: 8
lettuce:
pool:
maxActive: 8
maxIdle: 100
minIdle: 10
maxWait: -1Global filter definition:
@Component
public class BrushProofFilter implements GlobalFilter, Ordered {
private final ReactiveStringRedisTemplate reactiveStringRedisTemplate;
public BrushProofFilter(ReactiveStringRedisTemplate reactiveStringRedisTemplate) {
this.reactiveStringRedisTemplate = reactiveStringRedisTemplate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// Get client IP
InetAddress address = exchange.getRequest().getRemoteAddress().getAddress();
// Get request URI
String path = exchange.getRequest().getPath().toString();
// Combine as Redis key
String key = ("ratelimiter:" + address + ":" + path);
// Set limit: 10 requests per 30 seconds
return this.reactiveStringRedisTemplate.opsForValue()
.setIfAbsent(key, "10", Duration.ofSeconds(30))
.flatMap(exist -> {
return this.reactiveStringRedisTemplate.opsForValue().decrement(key);
})
.doOnNext(num -> {
if (num < 0) {
throw new BrushProofException("You are accessing too fast");
}
})
.then(chain.filter(exchange));
}
@Override
public int getOrder() {
return -2;
}
}Custom exception:
public class BrushProofException extends RuntimeException {
private static final long serialVersionUID = 1L;
public BrushProofException(String message) {
super(message);
}
}Custom exception handler:
@Component
public class RatelimiterWebExceptionHandler implements WebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (ex instanceof RatelimiterException re) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
response.getHeaders().add("Content-Type", "text/html;charset=utf8");
return response.writeWith(Mono.just(response.bufferFactory().wrap(("You are accessing too fast").getBytes())));
}
return Mono.error(ex);
}
}Test results (images illustrate the request key and rate limiting behavior).
When the request count exceeds 10, access is blocked.
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.
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.
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.
