Mastering Rate Limiting in Spring Cloud Gateway with Redis and Custom Configurations
This guide explains how Spring Cloud Gateway's RequestRateLimiter works, how to configure Redis‑based token‑bucket throttling, customize key resolution, and implement a custom RateLimiter bean, providing detailed code examples and configuration tips for robust backend traffic control.
Overview
The RequestRateLimiter gateway filter uses a RateLimiter implementation to decide whether a request may proceed; otherwise it returns HTTP 429 – Too Many Requests . The filter accepts an optional keyResolver and rate‑limiter‑specific parameters to generate a key (e.g., a Redis key).
The KeyResolver is a bean implementing the KeyResolver interface. In configuration you reference it with SpEL, e.g., #{@userKeyResolver}. The interface is defined as:
public interface KeyResolver {
Mono<String> resolve(ServerWebExchange exchange);
}If no key is found, the request is denied by default. You can adjust this behavior with
spring.cloud.gateway.filter.request-rate-limiter.deny-empty-keyand empty-key-status-code.
Note: The RequestRateLimiter cannot be configured using shortcut notation; the following example is invalid:
# INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}Using Redis for Rate Limiting
Redis support relies on spring-boot-starter-data-redis-reactive and uses the token‑bucket algorithm.
Configuration properties:
redis-rate-limiter.replenishRate // tokens added per second (bucket fill rate)
redis-rate-limiter.burstCapacity // maximum tokens the bucket can hold; zero blocks all requests
redis-rate-limiter.requestedTokens // tokens consumed per request (default 1)Typical settings:
Set replenishRate and burstCapacity to the same value for a stable rate.
Set burstCapacity higher than replenishRate to allow temporary bursts.
The limiter leaves a pause between bursts based on replenishRate; consecutive bursts may cause dropped requests (HTTP 429). For rates below 1 request/second, configure replenishRate to the desired request count, set requestedTokens to the time span in seconds, and set burstCapacity to replenishRate * requestedTokens (e.g., 1 rps, requestedTokens=60, burstCapacity=60 yields 1 request per minute).
Example YAML configuration:
spring:
cloud:
gateway:
default-filters:
- StripPrefix=1
routes:
- id: o001
uri: lb://order-service
predicates:
- Path=/api-a/**, /api-b/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 2
redis-rate-limiter.requestedTokens: 1
keyResolver: "#{@userKeyResolver}"
#statusCode: INTERNAL_SERVER_ERRORKey resolver bean example:
@Configuration
public class RedisRateConfig {
@Bean
public KeyResolver userKeyResolver() {
// IP‑based limiting
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
}This defines a limit of 1 request per second per IP with a burst of 2. The KeyResolver simply extracts the client IP (not recommended for production without additional safeguards).
Custom Rate Limiter
You can also define a custom rate limiter by implementing the RateLimiter interface and referencing it via SpEL, e.g., #{@userRateLimiter}. The following YAML uses a custom limiter together with the previously defined KeyResolver:
spring:
cloud:
gateway:
default-filters:
- StripPrefix=1
routes:
- id: o001
uri: lb://order-service
predicates:
- Path=/api-a/**, /api-b/**
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@userRateLimiter}"
keyResolver: "#{@userKeyResolver}"Custom limiter implementation skeleton:
public class UserRateLimiter implements RateLimiter<UserRateLimiter.Config> {
@Override
public Map<String, Config> getConfig() { return null; }
@Override
public Class<Config> getConfigClass() { return null; }
@Override
public Config newConfig() { return null; }
@Override
public Mono<Response> isAllowed(String routeId, String id) { return null; }
public static class Config {
@Min(1)
private int replenishRate;
@Min(0)
private int burstCapacity = 1;
@Min(1)
private int requestedTokens = 1;
// getters and setters omitted for brevity
@Override
public String toString() {
return new ToStringCreator(this)
.append("replenishRate", replenishRate)
.append("burstCapacity", burstCapacity)
.append("requestedTokens", requestedTokens)
.toString();
}
}
}End of guide.
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.
