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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
@Service public class DistributedRateLimitService { @Autowired private RedisTemplate<String, Object> redisTemplate; private static final Logger logger = LoggerFactory.getLogger(DistributedRateLimitService.class); private static final String RATE_LIMIT_KEY_PREFIX = "rate_limit:"; private static final String RATE_LIMIT_CONFIG_KEY_PREFIX = "rate_limit_config:";
public RateLimitResult checkDistributedRateLimit(String key, RateLimiterConfig config) { try { String rateLimitKey = RATE_LIMIT_KEY_PREFIX + key; String configKey = RATE_LIMIT_CONFIG_KEY_PREFIX + key; redisTemplate.opsForValue().set(configKey, config, Duration.ofHours(1)); String luaScript = buildRateLimitLuaScript(); List<Object> result = redisTemplate.execute(new DefaultRedisScript<>(luaScript, List.class), Collections.singletonList(rateLimitKey), config.getPermitsPerSecond(), System.currentTimeMillis() / 1000, config.getMaxBurst() ); boolean allowed = (Boolean) result.get(0); double availablePermits = (Double) result.get(1); RateLimitResult rateLimitResult = new RateLimitResult(); rateLimitResult.setApiKey(key); rateLimitResult.setAllowed(allowed); rateLimitResult.setRate(config.getPermitsPerSecond()); rateLimitResult.setAvailablePermits(availablePermits); rateLimitResult.setTimestamp(System.currentTimeMillis()); if (!allowed) { rateLimitResult.setMessage("分布式限流:请求频率过高"); } logger.debug("分布式限流检查: key={}, allowed={}, availablePermits={}", key, allowed, availablePermits); return rateLimitResult; } catch (Exception e) { logger.error("分布式限流检查失败: key={}", key, e); return RateLimitResult.rejected("分布式限流检查失败"); } }
private String buildRateLimitLuaScript() { return """ local key = KEYS[1] local permitsPerSecond = tonumber(ARGV[1]) local currentTime = tonumber(ARGV[2]) local maxBurst = tonumber(ARGV[3]) local bucket = redis.call('HMGET', key, 'tokens', 'lastRefillTime') local tokens = tonumber(bucket[1]) or maxBurst local lastRefillTime = tonumber(bucket[2]) or currentTime -- 计算时间差和需要补充的令牌数 local timePassed = currentTime - lastRefillTime local tokensToAdd = timePassed * permitsPerSecond tokens = math.min(tokens + tokensToAdd, maxBurst) -- 检查是否有足够的令牌 if tokens >= 1 then tokens = tokens - 1 redis.call('HMSET', key, 'tokens', tokens, 'lastRefillTime', currentTime) redis.call('EXPIRE', key, 3600) return {true, tokens} else redis.call('HMSET', key, 'tokens', tokens, 'lastRefillTime', currentTime) redis.call('EXPIRE', key, 3600) return {false, tokens} end """; }
public RateLimiterStatus getDistributedRateLimitStatus(String key) { try { String rateLimitKey = RATE_LIMIT_KEY_PREFIX + key; String configKey = RATE_LIMIT_CONFIG_KEY_PREFIX + key; RateLimiterConfig config = (RateLimiterConfig) redisTemplate.opsForValue().get(configKey); if (config == null) { return null; } Map<Object, Object> bucket = redisTemplate.opsForHash().entries(rateLimitKey); if (bucket.isEmpty()) { RateLimiterStatus status = new RateLimiterStatus(); status.setRate(config.getPermitsPerSecond()); status.setAvailablePermits(config.getMaxBurst()); return status; } double tokens = Double.parseDouble(bucket.get("tokens").toString()); long lastRefillTime = Long.parseLong(bucket.get("lastRefillTime").toString()); long currentTime = System.currentTimeMillis() / 1000; double timePassed = currentTime - lastRefillTime; double tokensToAdd = timePassed * config.getPermitsPerSecond(); double availablePermits = Math.min(tokens + tokensToAdd, config.getMaxBurst()); RateLimiterStatus status = new RateLimiterStatus(); status.setRate(config.getPermitsPerSecond()); status.setAvailablePermits(availablePermits); status.setCurrentTime(System.currentTimeMillis()); return status; } catch (Exception e) { logger.error("获取分布式限流状态失败: key={}", key, e); return null; } }
public void cleanupDistributedRateLimit(String key) { try { String rateLimitKey = RATE_LIMIT_KEY_PREFIX + key; String configKey = RATE_LIMIT_CONFIG_KEY_PREFIX + key; redisTemplate.delete(rateLimitKey); redisTemplate.delete(configKey); logger.info("分布式限流数据清理完成: key={}", key); } catch (Exception e) { logger.error("清理分布式限流数据失败: key={}", key, e); } } }
|