Skip to content

Commit 9b82081

Browse files
authored
Fix for redis rate limiter not working with multiple routes. Fixes gh-2288 (#2517)
1 parent bf493a3 commit 9b82081

File tree

2 files changed

+34
-8
lines changed

2 files changed

+34
-8
lines changed

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* @author Spencer Gibb
4949
* @author Ronny Bräunlich
5050
* @author Denis Cutic
51+
* @author Andrey Muchnik
5152
*/
5253
@ConfigurationProperties("spring.cloud.gateway.redis-rate-limiter")
5354
public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Config> implements ApplicationContextAware {
@@ -145,16 +146,16 @@ public RedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity, int
145146
this.defaultConfig.setRequestedTokens(defaultRequestedTokens);
146147
}
147148

148-
static List<String> getKeys(String id) {
149+
static List<String> getKeys(String id, String routeId) {
149150
// use `{}` around keys to use Redis Key hash tags
150151
// this allows for using redis cluster
151152

152-
// Make a unique key per user.
153-
String prefix = "request_rate_limiter.{" + id;
153+
// Make a unique key per user and route.
154+
String prefix = "request_rate_limiter.{" + routeId + "." + id + "}.";
154155

155156
// You need two Redis keys for Token Bucket.
156-
String tokenKey = prefix + "}.tokens";
157-
String timestampKey = prefix + "}.timestamp";
157+
String tokenKey = prefix + "tokens";
158+
String timestampKey = prefix + "timestamp";
158159
return Arrays.asList(tokenKey, timestampKey);
159160
}
160161

@@ -245,7 +246,7 @@ public Mono<Response> isAllowed(String routeId, String id) {
245246
int requestedTokens = routeConfig.getRequestedTokens();
246247

247248
try {
248-
List<String> keys = getKeys(id);
249+
List<String> keys = getKeys(id, routeId);
249250

250251
// The arguments to the LUA script. time() returns unixtime in seconds.
251252
List<String> scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", "", requestedTokens + "");

spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
* @author Spencer Gibb
5252
* @author Ronny Bräunlich
5353
* @author Denis Cutic
54+
* @author Andrey Muchnik
5455
*/
5556
@SpringBootTest(webEnvironment = RANDOM_PORT)
5657
@DirtiesContext
@@ -98,6 +99,30 @@ public void redisRateLimiterWorks() throws Exception {
9899
checkLimitEnforced(id, replenishRate, burstCapacity, requestedTokens, routeId);
99100
}
100101

102+
103+
@Test
104+
public void redisRateLimiterWorksForMultipleRoutes() throws Exception {
105+
String id = UUID.randomUUID().toString();
106+
107+
int replenishRate = 10;
108+
int burstCapacity = 2 * replenishRate;
109+
int requestedTokens = 1;
110+
111+
String firstRouteId = "myroute";
112+
String secondRouteId = "myroute2";
113+
var config = rateLimiter.getConfig();
114+
config.put(firstRouteId, new RedisRateLimiter.Config().setBurstCapacity(burstCapacity)
115+
.setReplenishRate(replenishRate)
116+
.setRequestedTokens(requestedTokens));
117+
118+
config.put(secondRouteId, new RedisRateLimiter.Config().setBurstCapacity(burstCapacity)
119+
.setReplenishRate(replenishRate)
120+
.setRequestedTokens(requestedTokens));
121+
122+
checkLimitEnforced(id, replenishRate, burstCapacity, requestedTokens, firstRouteId);
123+
checkLimitEnforced(id, replenishRate, burstCapacity, requestedTokens, secondRouteId);
124+
}
125+
101126
@Test
102127
public void redisRateLimiterWorksForLowRates() throws Exception {
103128
String id = UUID.randomUUID().toString();
@@ -137,8 +162,8 @@ public void redisRateLimiterWorksForZeroBurstCapacity() throws Exception {
137162

138163
@Test
139164
public void keysUseRedisKeyHashTags() {
140-
assertThat(RedisRateLimiter.getKeys("1")).containsExactly("request_rate_limiter.{1}.tokens",
141-
"request_rate_limiter.{1}.timestamp");
165+
assertThat(RedisRateLimiter.getKeys("1", "routeId")).containsExactly("request_rate_limiter.{routeId.1}.tokens",
166+
"request_rate_limiter.{routeId.1}.timestamp");
142167
}
143168

144169
@Test

0 commit comments

Comments
 (0)