diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java index 34aae6bd..999b8d5c 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiter.java @@ -10,6 +10,9 @@ import javax.validation.constraints.Min; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -18,9 +21,6 @@ import org.springframework.data.redis.core.script.RedisScript; import org.springframework.validation.Validator; import org.springframework.validation.annotation.Validated; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - /** * See https://stripe.com/blog/rate-limiters and * https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L11-L34 @@ -102,11 +102,8 @@ public class RedisRateLimiter extends AbstractRateLimiter keys = getKeys(id); - // You need two Redis keys for Token Bucket. - List keys = Arrays.asList(prefix + ".tokens", prefix + ".timestamp"); // The arguments to the LUA script. time() returns unixtime in seconds. List scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", @@ -141,6 +138,19 @@ public class RedisRateLimiter extends AbstractRateLimiter getKeys(String id) { + // 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; + + // You need two Redis keys for Token Bucket. + String tokenKey = prefix + "}.tokens"; + String timestampKey = prefix + "}.timestamp"; + return Arrays.asList(tokenKey, timestampKey); + } + @Validated public static class Config { @Min(1) diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java index a850713c..bf85e021 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java @@ -71,6 +71,12 @@ public class RedisRateLimiterTests extends BaseWebClientTests { response = rateLimiter.isAllowed(routeId, id).block(); assertThat(response.isAllowed()).as("steady state # %s is allowed", replenishRate).isFalse(); } + + @Test + public void keysUseRedisKeyHashTags() { + assertThat(RedisRateLimiter.getKeys("1")) + .containsExactly("request_rate_limiter.{1}.tokens", "request_rate_limiter.{1}.timestamp"); + } @EnableAutoConfiguration @SpringBootConfiguration