悠悠楠杉
Java限流实现:5种实用流量控制方法解析
引言:为什么需要限流?
在分布式系统高并发场景下,限流是保护系统稳定的重要防线。当每秒请求量突破阈值时,可能导致服务器资源耗尽、响应延迟飙升,甚至引发雪崩效应。本文将深入讲解Java实现的5种主流限流方案,包含代码示例和适用场景分析。
一、计数器限流法(固定窗口)
最基础的限流实现,适合简单场景:
java
public class CounterLimiter {
private final AtomicInteger counter = new AtomicInteger(0);
private final int limit;
private long lastResetTime = System.currentTimeMillis();
public CounterLimiter(int limitPerSecond) {
this.limit = limitPerSecond;
}
public boolean tryAcquire() {
long now = System.currentTimeMillis();
if (now - lastResetTime > 1000) {
counter.set(0);
lastResetTime = now;
}
return counter.incrementAndGet() <= limit;
}
}
特点分析:
- 优点:实现简单,内存消耗低
- 缺点:窗口临界点可能出现双倍流量(如第999ms和第1001ms各允许100次请求)
二、滑动窗口限流
优化计数器法的临界问题,典型实现如Redis+Lua:
java
// 使用Redis实现的滑动窗口
public boolean isAllowed(String key, int windowSize, int limit) {
long now = System.currentTimeMillis();
long windowStart = now - windowSize * 1000L;
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
long count = redisTemplate.opsForZSet().count(key, windowStart, now);
if (count < limit) {
redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);
return true;
}
return false;
}
适用场景:分布式系统环境下的精准限流
三、漏桶算法
恒定速率处理请求,适合流量整形:
java
public class LeakyBucket {
private final long capacity;
private final long leakRate; // 毫秒/滴
private long waterLevel;
private long lastLeakTime;
public synchronized boolean tryConsume() {
leakWater();
if (waterLevel < capacity) {
waterLevel++;
return true;
}
return false;
}
private void leakWater() {
long now = System.currentTimeMillis();
long elapsed = now - lastLeakTime;
waterLevel = Math.max(0, waterLevel - elapsed * leakRate);
lastLeakTime = now;
}
}
核心优势:能绝对保证处理速率不超过设定值
四、令牌桶算法(推荐)
兼顾灵活性和保护性,Guava RateLimiter的实现原理:
java
public class TokenBucket {
private final int capacity;
private double tokens;
private long lastRefillTime;
private final double refillRate; // tokens/ms
public synchronized boolean tryAcquire(int tokensNeeded) {
refillTokens();
if (tokens >= tokensNeeded) {
tokens -= tokensNeeded;
return true;
}
return false;
}
private void refillTokens() {
long now = System.currentTimeMillis();
double elapsed = now - lastRefillTime;
tokens = Math.min(capacity, tokens + elapsed * refillRate);
lastRefillTime = now;
}
}
业务场景:
- API调用频率限制
- 突发流量吸收(允许短时间内超过平均速率)
五、分布式限流实践
结合Nginx+Redis+Lua的三层防护:
java
// Spring Cloud Gateway限流配置
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(
10, // 每秒10个令牌
20, // 桶容量20
Duration.ofSeconds(1)
);
}
// 路由配置
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
方案选型指南
- 单机简单场景:计数器/滑动窗口
- 需要处理突发流量:令牌桶
- 严格恒定速率:漏桶算法
- 分布式系统:Redis+Lua实现
性能优化Tip:
- 使用LongAdder替代AtomicInteger
- 考虑分段锁优化
- 预热机制应对冷启动
结语
合理的限流策略需要结合业务特性,通常建议在网关层做全局限流,在方法级做细粒度控制。实际生产环境中,建议结合监控系统动态调整阈值,既保护系统又不影响正常业务流量。