Fix for redis rate limiter not working with multiple routes. Fixes gh-2288 (#2517)
This commit is contained in:
@@ -48,6 +48,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
* @author Spencer Gibb
|
||||
* @author Ronny Bräunlich
|
||||
* @author Denis Cutic
|
||||
* @author Andrey Muchnik
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.gateway.redis-rate-limiter")
|
||||
public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Config> implements ApplicationContextAware {
|
||||
@@ -145,16 +146,16 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
this.defaultConfig.setRequestedTokens(defaultRequestedTokens);
|
||||
}
|
||||
|
||||
static List<String> getKeys(String id) {
|
||||
static List<String> getKeys(String id, String routeId) {
|
||||
// use `{}` around keys to use Redis Key hash tags
|
||||
// this allows for using redis cluster
|
||||
|
||||
// Make a unique key per user.
|
||||
String prefix = "request_rate_limiter.{" + id;
|
||||
// Make a unique key per user and route.
|
||||
String prefix = "request_rate_limiter.{" + routeId + "." + id + "}.";
|
||||
|
||||
// You need two Redis keys for Token Bucket.
|
||||
String tokenKey = prefix + "}.tokens";
|
||||
String timestampKey = prefix + "}.timestamp";
|
||||
String tokenKey = prefix + "tokens";
|
||||
String timestampKey = prefix + "timestamp";
|
||||
return Arrays.asList(tokenKey, timestampKey);
|
||||
}
|
||||
|
||||
@@ -245,7 +246,7 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
int requestedTokens = routeConfig.getRequestedTokens();
|
||||
|
||||
try {
|
||||
List<String> keys = getKeys(id);
|
||||
List<String> keys = getKeys(id, routeId);
|
||||
|
||||
// The arguments to the LUA script. time() returns unixtime in seconds.
|
||||
List<String> scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", "", requestedTokens + "");
|
||||
|
||||
@@ -51,6 +51,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
||||
* @author Spencer Gibb
|
||||
* @author Ronny Bräunlich
|
||||
* @author Denis Cutic
|
||||
* @author Andrey Muchnik
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
@@ -98,6 +99,30 @@ public class RedisRateLimiterTests extends BaseWebClientTests {
|
||||
checkLimitEnforced(id, replenishRate, burstCapacity, requestedTokens, routeId);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void redisRateLimiterWorksForMultipleRoutes() throws Exception {
|
||||
String id = UUID.randomUUID().toString();
|
||||
|
||||
int replenishRate = 10;
|
||||
int burstCapacity = 2 * replenishRate;
|
||||
int requestedTokens = 1;
|
||||
|
||||
String firstRouteId = "myroute";
|
||||
String secondRouteId = "myroute2";
|
||||
var config = rateLimiter.getConfig();
|
||||
config.put(firstRouteId, new RedisRateLimiter.Config().setBurstCapacity(burstCapacity)
|
||||
.setReplenishRate(replenishRate)
|
||||
.setRequestedTokens(requestedTokens));
|
||||
|
||||
config.put(secondRouteId, new RedisRateLimiter.Config().setBurstCapacity(burstCapacity)
|
||||
.setReplenishRate(replenishRate)
|
||||
.setRequestedTokens(requestedTokens));
|
||||
|
||||
checkLimitEnforced(id, replenishRate, burstCapacity, requestedTokens, firstRouteId);
|
||||
checkLimitEnforced(id, replenishRate, burstCapacity, requestedTokens, secondRouteId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void redisRateLimiterWorksForLowRates() throws Exception {
|
||||
String id = UUID.randomUUID().toString();
|
||||
@@ -137,8 +162,8 @@ public class RedisRateLimiterTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void keysUseRedisKeyHashTags() {
|
||||
assertThat(RedisRateLimiter.getKeys("1")).containsExactly("request_rate_limiter.{1}.tokens",
|
||||
"request_rate_limiter.{1}.timestamp");
|
||||
assertThat(RedisRateLimiter.getKeys("1", "routeId")).containsExactly("request_rate_limiter.{routeId.1}.tokens",
|
||||
"request_rate_limiter.{routeId.1}.timestamp");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user