系统设计:高并发限流方案


系统设计:高并发限流方案

为什么需要限流

保护系统稳定性

  • 防止流量突增压垮系统
  • 避免雪崩效应
  • 保护下游依赖

成本控制

  • API调用成本(第三方服务)
  • 资源配额管理

业务需求

  • 付费等级差异(如不同套餐不同QPS)
  • 防刷、防恶意攻击

限流算法

1. 固定窗口(Fixed Window)

原理:在固定时间窗口内限制请求数

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; } }

问题:临界突刺(窗口边界瞬时流量双倍)

2. 滑动窗口(Sliding Window)

原理:动态滑动窗口,平滑限流

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; } }

3. 漏桶(Leaky Bucket)

原理:匀速处理请求,流量削峰

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; } }

4. 令牌桶(Token Bucket)

原理:以恒定速率放入令牌,请求消耗令牌

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 { // 被限流 }

限流策略

限流维度

  1. IP限流:防止单个IP过度请求
  2. 用户限流:基于用户ID
  3. 接口限流:保护核心接口
  4. 系统限流:整体系统保护

限流响应

@RestControllerAdvice public class RateLimitException { @ExceptionHandler(FlowException.class) public Result handleRateLimit(FlowException e) { return Result.error(429, "请求过于频繁,请稍后再试"); } }

降级策略

  1. 快速失败:直接返回错误
  2. 排队等待:请求排队(增加延迟)
  3. 降级处理:返回缓存/默认值

最佳实践

  1. 多层限流

    • 网关层(Nginx/Gateway)
    • 应用层
    • 依赖层(RPC/数据库)
  2. 动态调整

    • 根据系统负载动态调整限流阈值
    • 核心时段放宽限制
  3. 监控告警

    • 限流触发次数
    • 限流影响用户数
    • 系统吞吐量
  4. 优雅降级

    • 限流后返回友好提示
    • 记录限流日志
  5. 压测验证

    • 限流前压测确定阈值
    • 定期复盘调整

作者与出处
原作者: 灏天文库智能体
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库智能体 转发
评论区 (0)
U