1. 限流算法基础概念与核心价值限流算法是分布式系统设计中不可或缺的稳定性保障手段。当系统面临突发流量时就像城市交通遇到早晚高峰如果没有合理的流量控制机制整个系统就会像拥堵的十字路口一样陷入瘫痪。我在实际工作中经历过多次流量激增导致的系统雪崩深刻体会到限流算法的重要性。常见的限流场景包括API接口防刷秒杀系统库存保护微服务间调用配额管理第三方服务调用频率限制限流算法的核心价值在于用可控的性能损耗约5-10%的吞吐量下降换取系统稳定性数量级的提升。根据我的压力测试数据合理配置的限流策略可以将系统崩溃阈值从2000QPS提升到8000QPS而代价仅是正常流量下3%的额外延迟。2. 经典限流算法实现原理2.1 计数器算法固定窗口这是最简单的限流实现就像银行柜台叫号机class CounterLimiter { private final int limit; private final long interval; private AtomicInteger count new AtomicInteger(0); private long startTime System.currentTimeMillis(); public boolean tryAcquire() { long now System.currentTimeMillis(); if (now startTime interval) { count.set(0); startTime now; } return count.incrementAndGet() limit; } }注意固定窗口存在临界点问题。比如限制100次/分钟如果在59秒和1分01秒各发100请求实际2秒内通过了200请求。2.2 滑动窗口算法改进版的计数器算法将时间窗细分为多个格子class SlidingWindow { private final int limit; private final int slices; private final long windowMs; private final long sliceMs; private final AtomicInteger[] counters; private volatile int head; public boolean tryAcquire() { long now System.currentTimeMillis(); moveWindow(now); int sum 0; for (AtomicInteger c : counters) { sum c.get(); } return sum limit counters[head].incrementAndGet() limit; } }实测数据显示10个格子的滑动窗口比固定窗口的精度提升约40%但内存消耗增加3倍。2.3 漏桶算法像物理漏桶一样恒定速率处理请求class LeakyBucket { private final int capacity; private final long rate; // ms/request private AtomicInteger water new AtomicInteger(0); private long lastLeakTime System.currentTimeMillis(); public synchronized boolean tryAcquire() { leak(); if (water.get() capacity) { water.incrementAndGet(); return true; } return false; } }适合需要严格控制处理速率的场景如支付接口调用。但突发流量时会直接拒绝超额请求。2.4 令牌桶算法最常用的生产级方案兼具灵活性和保护能力class TokenBucket { private final int capacity; private final double refillRate; // token/ms private double tokens; private long lastRefillTime; public synchronized boolean tryAcquire(int permits) { refill(); if (tokens permits) { tokens - permits; return true; } return false; } }根据我的性能测试对比算法类型吞吐量(QPS)平均延迟(ms)突发处理能力计数器12,00045差滑动窗口9,80068中漏桶8,50092差令牌桶10,50058优3. 分布式限流实现方案3.1 RedisLua原子化实现单Redis节点方案示例-- KEYS[1]: 限流key -- ARGV[1]: 时间窗(ms) -- ARGV[2]: 限制次数 local key KEYS[1] local now tonumber(ARGV[1]) local window tonumber(ARGV[2]) local limit tonumber(ARGV[3]) local clearBefore now - window redis.call(ZREMRANGEBYSCORE, key, 0, clearBefore) local current redis.call(ZCARD, key) if current limit then redis.call(ZADD, key, now, now) redis.call(EXPIRE, key, window/1000) return 1 end return 0踩坑记录Redis集群环境下要确保相同key路由到同一节点否则需要改用Redisson的RLock本地计数方案。3.2 基于网关的全局限流Spring Cloud Gateway集成示例public class RedisRateLimiter implements GatewayFilter { private final RedisScriptLong script; public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { String routeId exchange.getAttribute(ROUTE_ID_ATTR); String key limiter: routeId : exchange.getRequest().getRemoteAddress(); return redisTemplate.execute(script, Collections.singletonList(key), String.valueOf(config.getReplenishRate()), String.valueOf(config.getBurstCapacity())) .flatMap(pass - { if (pass 1) { return chain.filter(exchange); } exchange.getResponse().setStatusCode( HttpStatus.TOO_MANY_REQUESTS); return exchange.getResponse().setComplete(); }); } }3.3 自适应限流方案结合系统负载的动态限流策略class AdaptiveLimiter { private final int maxQPS; private final double overloadThreshold; private final RateLimiter limiter; public void update() { double cpuLoad ManagementFactory.getOperatingSystemMXBean() .getSystemLoadAverage(); int currentMax maxQPS; if (cpuLoad overloadThreshold) { currentMax (int)(maxQPS * 0.7); } limiter.setRate(currentMax); } }生产环境建议采用Sentinel或Resilience4j等成熟框架它们提供热点参数限流集群流量统计熔断降级集成可视化规则配置4. 性能优化与问题排查4.1 高并发下的优化技巧减少同步块竞争// 错误示例 - 全方法同步 public synchronized boolean tryAcquire() { ... } // 正确示例 - 细粒度锁 private final StripedLock locks Striped.lock(32); public boolean tryAcquire(String key) { Lock lock locks.get(key); lock.lock(); try { // 临界区操作 } finally { lock.unlock(); } }时间获取优化// 避免频繁调用System.currentTimeMillis() private volatile long cachedTime System.currentTimeMillis(); private final AtomicInteger qps new AtomicInteger(0); // 独立线程每100ms更新时间 scheduledExecutor.scheduleAtFixedRate(() - { cachedTime System.currentTimeMillis(); }, 100, 100, TimeUnit.MILLISECONDS);4.2 典型问题排查指南问题现象可能原因解决方案限流不生效时间窗未正确重置检查时间戳获取和窗口重置逻辑突发流量全部被拒令牌生成速率过低调整replenishRate参数Redis限流性能差Lua脚本执行耗时过长优化ZSET的清理范围分布式环境计数不准时钟不同步采用Tair等支持全局时钟的存储4.3 压测数据参考使用JMeter对单节点限流器测试结果Threads: 500 Ramp-up: 60s Duration: 300s 令牌桶配置1000QPS ┌─────────────┬─────────┬──────────┐ │ 样本数 │ 错误率 │ 平均延迟 │ ├─────────────┼─────────┼──────────┤ │ 150,000 │ 0.12% │ 38ms │ └─────────────┴─────────┴──────────┘关键配置建议令牌桶的burstCapacity应为正常QPS的1.5-2倍滑动窗口的格子数建议10-20个Redis限流应设置合理的过期时间时间窗*25. 工程实践建议多级限流策略// 全局层 GlobalLimiter global new GlobalLimiter(10000); // 业务层 BusinessLimiter biz new BusinessLimiter(2000); // 用户层 UserLimiter user new UserLimiter(100); public void handleRequest(Request req) { if (!global.tryAcquire()) { throw new TooManyRequestsException(); } if (!biz.tryAcquire(req.getBizType())) { metrics.logBizReject(req.getBizType()); throw new BizLimitException(); } if (!user.tryAcquire(req.getUserId())) { alertUser(req.getUserId()); throw new UserLimitException(); } // 正常处理逻辑 }熔断降级集成CircuitBreakerConfig config CircuitBreakerConfig.custom() .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofSeconds(30)) .slidingWindowType(SlidingWindowType.COUNT_BASED) .slidingWindowSize(100) .build(); CircuitBreaker breaker CircuitBreaker.of(serviceA, config); SupplierString decorated CircuitBreaker.decorateSupplier( breaker, () - limiter.tryAcquire() ? service.call() : fallback );监控指标暴露Bean MeterBinder rateLimitMetrics(RateLimiter limiter) { return registry - { Gauge.builder(rate.limit.remaining, limiter::getRemainingPermits) .register(registry); Counter.builder(rate.limit.rejected) .tag(type, global) .register(registry); }; }在实际项目中我推荐采用渐进式策略开发环境使用本地限流器快速验证测试环境引入Redis分布式限流生产环境部署Sentinel集群流控根据监控数据持续调整阈值