How to Implement Multi-Dimensional Rate Limiting in Spring Cloud Gateway
This guide explains how Spring Cloud Gateway replaces Zuul and provides Redis‑based multi‑dimensional rate limiting, covering Maven dependencies, YAML configuration, custom key resolver beans, stress testing, Redis key monitoring, core Java implementation, and the underlying Lua script.
About Spring Cloud Gateway
Spring Cloud Gateway is the official Spring gateway built on Spring 5, Spring Boot 2 and Project Reactor, providing a simple and effective way to route APIs. It aims to replace Netflix Zuul and offers unified routing, filter‑chain based features such as security, monitoring, and rate limiting.
Reference to Zuul Multi‑Dimensional Rate Limiting
For details on Zuul’s multi‑dimensional rate limiting, see the linked article.
Starting Gateway Rate Limiting
POM Dependencies
<!--spring cloud gateway dependency-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--reactive Redis dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>IP‑Based Rate Limiting Configuration
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: lb://pigx-upms
order: 10000
predicates:
- Path=/admin/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 3
key-resolver: "#{@remoteAddrKeyResolver}"
- StripPrefix=1Bean for Multi‑Dimensional Key Resolver
@Bean
KeyResolver remoteAddrKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}Stress Test
Concurrent 5 threads.
Redis Data Changes
Monitoring Redis with the monitor command shows two keys:
requestratelimiter.{xxx}.timestamp
requestratelimiter.{xxx}.tokens
Implementation Principle
Spring Cloud Gateway uses a Redis‑based rate limiter by default; custom implementations only need to implement the RateLimiter interface.
Core RedisRateLimiter Code
public Mono<Response> isAllowed(String routeId, String id) {
Config routeConfig = getConfig().getOrDefault(routeId, defaultConfig);
int replenishRate = routeConfig.getReplenishRate();
int burstCapacity = routeConfig.getBurstCapacity();
try {
List<String> keys = getKeys(id);
List<String> scriptArgs = Arrays.asList(
replenishRate + "", burstCapacity + "", Instant.now().getEpochSecond() + "", "1");
// Execute Redis LUA script
Flux<List<Long>> flux = this.redisTemplate.execute(this.script, keys, scriptArgs);
return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L)))
.reduce(new ArrayList<>(), (longs, l) -> { longs.addAll(l); return longs; })
.map(results -> {
boolean allowed = results.get(0) == 1L;
Long tokensLeft = results.get(1);
Response response = new Response(allowed, getHeaders(routeConfig, tokensLeft));
if (log.isDebugEnabled()) {
log.debug("response: " + response);
}
return response;
});
} catch (Exception e) {
log.error("Error determining if user allowed from redis", e);
return Mono.just(new Response(true, getHeaders(routeConfig, -1L)));
}
}LUA Script
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
