1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| @Component public class RateLimiterService { @Autowired private RedisTemplate<String, Object> redisTemplate;
public boolean isAllowed(String key, int limit, int windowSizeInSeconds) { long now = System.currentTimeMillis(); long windowStart = now - windowSizeInSeconds * 1000; String script = "local key = KEYS[1] " + "local window = tonumber(ARGV[1]) " + "local limit = tonumber(ARGV[2]) " + "local now = tonumber(ARGV[3]) " + "redis.call('ZREMRANGEBYSCORE', key, 0, now - window) " + "local count = redis.call('ZCARD', key) " + "if count < limit then " + " redis.call('ZADD', key, now, now) " + " redis.call('EXPIRE', key, window) " + " return 1 " + "else " + " return 0 " + "end"; DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText(script); redisScript.setResultType(Long.class); Long result = redisTemplate.execute(redisScript, Collections.singletonList("rate_limit:" + key), String.valueOf(windowSizeInSeconds * 1000), String.valueOf(limit), String.valueOf(now)); return result != null && result == 1; }
public boolean tryAcquire(String key, int capacity, int refillRate) { String script = "local key = KEYS[1] " + "local capacity = tonumber(ARGV[1]) " + "local refillRate = tonumber(ARGV[2]) " + "local now = tonumber(ARGV[3]) " + "local tokens = redis.call('HMGET', key, 'tokens', 'lastRefill') " + "local currentTokens = tonumber(tokens[1]) or capacity " + "local lastRefill = tonumber(tokens[2]) or now " + "local timePassed = now - lastRefill " + "local tokensToAdd = math.floor(timePassed * refillRate / 1000) " + "currentTokens = math.min(capacity, currentTokens + tokensToAdd) " + "if currentTokens >= 1 then " + " currentTokens = currentTokens - 1 " + " redis.call('HMSET', key, 'tokens', currentTokens, 'lastRefill', now) " + " redis.call('EXPIRE', key, 3600) " + " return 1 " + "else " + " redis.call('HMSET', key, 'tokens', currentTokens, 'lastRefill', now) " + " redis.call('EXPIRE', key, 3600) " + " return 0 " + "end"; DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText(script); redisScript.setResultType(Long.class); Long result = redisTemplate.execute(redisScript, Collections.singletonList("token_bucket:" + key), String.valueOf(capacity), String.valueOf(refillRate), String.valueOf(System.currentTimeMillis())); return result != null && result == 1; } }
|