保护系统稳定性:
成本控制:
业务需求:
原理:在固定时间窗口内限制请求数
public class FixedWindowRateLimiter { private final int limit; // 限制数量 private final long windowSizeMs; // 窗口大小 private final AtomicInteger counter; private volatile long windowStart; public boolean allowRequest() { long now = System.currentTimeMillis(); // 重置窗口 if (now - windowStart >= windowSizeMs) { synchronized (this) { if (now - windowStart >= windowSizeMs) { windowStart = now; counter.set(0); } } } int count = counter.incrementAndGet(); return count <= limit; } }
问题:临界突刺(窗口边界瞬时流量双倍)
原理:动态滑动窗口,平滑限流
public class SlidingWindowRateLimiter { private final int limit; private final long windowSizeMs; private final ConcurrentHashMap<Long, AtomicInteger> windows; public boolean allowRequest() { long now = System.currentTimeMillis(); long windowKey = now / 1000; // 秒级窗口 // 清理过期窗口 long expireTime = now - windowSizeMs; windows.entrySet().removeIf(entry -> entry.getKey() < expireTime); // 统计当前窗口请求数 int total = windows.values().stream() .mapToInt(AtomicInteger::get) .sum(); if (total >= limit) { return false; } windows.computeIfAbsent(windowKey, k -> new AtomicInteger()).incrementAndGet(); return true; } }
原理:匀速处理请求,流量削峰
public class LeakyBucketRateLimiter { private final long capacity; // 桶容量 private final long ratePerMs; // 漏水速率(每毫秒) private long water = 0; // 当前水量 private long lastLeakTime; public synchronized boolean allowRequest(int requestSize) { long now = System.currentTimeMillis(); long elapsed = now - lastLeakTime; // 漏水 long leaked = elapsed * ratePerMs; water = Math.max(0, water - leaked); lastLeakTime = now; // 加水 if (water + requestSize <= capacity) { water += requestSize; return true; } return false; } }
原理:以恒定速率放入令牌,请求消耗令牌
public class TokenBucketRateLimiter { private final long capacity; // 桶容量 private final long ratePerMs; // 令牌生成速率 private long tokens; private long lastRefillTime; public synchronized boolean allowRequest(int tokenNeeded) { long now = System.currentTimeMillis(); long elapsed = now - lastRefillTime; // 填充令牌 long newTokens = elapsed * ratePerMs; tokens = Math.min(capacity, tokens + newTokens); lastRefillTime = now; // 消耗令牌 if (tokens >= tokenNeeded) { tokens -= tokenNeeded; return true; } return false; } }
应用:Google Guava RateLimiter采用令牌桶算法
Guava RateLimiter:
// 创建限流器(每秒100个令牌) RateLimiter rateLimiter = RateLimiter.create(100.0); // 尝试获取令牌 if (rateLimiter.tryAcquire()) { // 处理请求 } else { // 拒绝请求 }
Resilience4j:
// 配置限流 RateLimiterConfig config = RateLimiterConfig.custom() .limitForPeriod(100) .limitRefreshPeriod(Duration.ofSeconds(1)) .timeoutDuration(Duration.ofMillis(100)) .build(); RateLimiterRegistry registry = RateLimiterRegistry.of(config); RateLimiter limiter = registry.rateLimiter("myService"); // 使用 if (limiter.acquirePermission()) { // 处理请求 }
Nginx限流:
# 漏桶限流 limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; server { location /api { limit_req zone=api_limit burst=20 nodelay; # burst: 允许突发20个请求 # nodelay: 不延迟处理 } }
Spring Cloud Gateway:
spring: cloud: gateway: routes: - id: rate_limiter_route uri: http://localhost:8080 filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20
Redis + Lua脚本(令牌桶):
-- redis_token_bucket.lua local key = KEYS[1] local capacity = tonumber(ARGV[1]) local rate = tonumber(ARGV[2]) local requested = tonumber(ARGV[3]) local now = tonumber(ARGV[4]) local info = redis.call('HMGET', key, 'tokens', 'last_time') local tokens = tonumber(info[1]) or capacity local last_time = tonumber(info[2]) or now -- 填充令牌 local delta = math.max(0, now - last_time) local new_tokens = math.min(capacity, tokens + delta * rate) -- 消耗令牌 if new_tokens >= requested then new_tokens = new_tokens - requested redis.call('HMSET', key, 'tokens', new_tokens, 'last_time', now) redis.call('EXPIRE', key, math.ceil(capacity / rate) + 1) return 1 else return 0 end
Sentinel(阿里开源):
// 定义限流规则 List<FlowRule> rules = new ArrayList<>(); FlowRule rule = new FlowRule(); rule.setResource("myAPI"); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(100); // QPS=100 rules.add(rule); FlowRuleManager.loadRules(rules); // 使用 if (SphU.entry("myAPI")) { try { // 处理请求 } finally { SphU.exit(); } } else { // 被限流 }
@RestControllerAdvice public class RateLimitException { @ExceptionHandler(FlowException.class) public Result handleRateLimit(FlowException e) { return Result.error(429, "请求过于频繁,请稍后再试"); } }
多层限流:
动态调整:
监控告警:
优雅降级:
压测验证: