限流是防止系统过载的重要手段,广泛应用于高并发场景。
1. 什么是限流算法?
定义
限流算法是一种用于控制请求流量的技术,防止系统因请求过多而过载。通过限制单位时间内允许通过的请求数量,可以有效保护系统资源,确保服务的稳定性和可用性。
2. 常见的限流算法
2.1 固定窗口计数器算法
原理:将时间分成固定大小的窗口,每个窗口内允许通过的请求数量固定。如果当前窗口的请求数量超过限制,则拒绝后续请求。
优点:
-
实现简单,性能高。
缺点:
-
在窗口切换时可能出现突发流量,导致限流不准确。
实际应用场景:
-
网关服务:限制每个用户的请求频率。
-
数据库访问:限制每秒的查询次数。
示例代码:
import java.util.concurrent.atomic.AtomicInteger;
public class FixedWindowRateLimiter {
private final int limit;
private final AtomicInteger counter = new AtomicInteger(0);
private long startTime = System.currentTimeMillis();
public FixedWindowRateLimiter(int limit) {
this.limit = limit;
}
public synchronized boolean allowRequest() {
long now = System.currentTimeMillis();
if (now - startTime >= 1000) { // 1秒窗口
counter.set(0);
startTime = now;
}
return counter.incrementAndGet() <= limit;
}
}
2.2 滑动窗口日志算法
原理:记录每个请求的时间戳,通过滑动窗口的方式计算单位时间内的请求数量。如果请求数量超过限制,则拒绝后续请求。
优点:
-
精确控制流量,避免突发流量问题。
缺点:
-
实现相对复杂,需要存储请求的时间戳。
实际应用场景:
-
API网关:限制每个用户的调用频率。
-
微服务:限制每个服务的调用频率。
示例代码:
import java.util.LinkedList;
import java.util.Queue;
public class SlidingWindowLogRateLimiter {
private final int limit;
private final Queue<Long> timestamps = new LinkedList<>();
private final long windowSize = 1000; // 1秒窗口
public SlidingWindowLogRateLimiter(int limit) {
this.limit = limit;
}
public synchronized boolean allowRequest() {
long now = System.currentTimeMillis();
while (!timestamps.isEmpty() && timestamps.peek() <= now - windowSize) {
timestamps.poll();
}
if (timestamps.size() < limit) {
timestamps.add(now);
return true;
}
return false;
}
}
2.3 漏桶算法
原理:通过一个漏桶来控制流量,漏桶以固定速率漏水,请求进入漏桶后按顺序流出。如果漏桶满了,则拒绝后续请求。
优点:
-
平滑流量,避免突发流量。
缺点:
-
实现相对复杂,需要维护一个队列。
实际应用场景:
-
网络流量控制:平滑网络流量。
-
任务调度:控制任务的执行速率。
示例代码:
import java.util.LinkedList;
import java.util.Queue;
public class LeakyBucketRateLimiter {
private final int capacity;
private final long leakRate;
private final Queue<Long> timestamps = new LinkedList<>();
private long lastLeakTime = System.currentTimeMillis();
public LeakyBucketRateLimiter(int capacity, long leakRate) {
this.capacity = capacity;
this.leakRate = leakRate; // 每秒漏水速率
}
public synchronized boolean allowRequest() {
long now = System.currentTimeMillis();
long elapsed = now - lastLeakTime;
lastLeakTime = now;
// 漏水
int leaked = (int) (elapsed * leakRate / 1000);
while (!timestamps.isEmpty() && leaked > 0) {
timestamps.poll();
leaked--;
}
if (timestamps.size() < capacity) {
timestamps.add(now);
return true;
}
return false;
}
}
2.4 令牌桶算法
原理:通过一个令牌桶来控制流量,令牌桶以固定速率生成令牌,请求需要消耗一个令牌才能通过。如果令牌桶为空,则拒绝后续请求。
优点:
-
精确控制流量,支持突发流量。
缺点:
-
实现相对复杂,需要维护令牌桶。
实际应用场景:
-
API网关:限制每个用户的调用频率。
-
微服务:限制每个服务的调用频率。
示例代码:
import java.util.concurrent.atomic.AtomicLong;
public class TokenBucketRateLimiter {
private final long rate;
private final long capacity;
private final AtomicLong tokens = new AtomicLong(0);
private long lastRefillTime = System.currentTimeMillis();
public TokenBucketRateLimiter(long rate, long capacity) {
this.rate = rate; // 每秒生成的令牌数
this.capacity = capacity;
this.tokens.set(capacity);
}
public synchronized boolean allowRequest() {
long now = System.currentTimeMillis();
long elapsed = now - lastRefillTime;
lastRefillTime = now;
// 填充令牌
long newTokens = elapsed * rate / 1000;
long currentTokens = Math.min(tokens.get() + newTokens, capacity);
tokens.set(currentTokens);
if (tokens.get() > 0) {
tokens.decrementAndGet();
return true;
}
return false;
}
}
3. 推荐书籍
3.1 《高性能架构设计》
-
作者:[Richardson, Sam]
-
内容简介:这本书详细介绍了高性能架构的设计原则和实践,包括限流、缓存、负载均衡等技术。
-
适用人群:系统架构师、高级开发工程师。
3.2 《分布式系统:概念与设计》
-
作者:[George Coulouris, Jean Dollimore, Tim Kindberg]
-
内容简介:这本书涵盖了分布式系统的基础知识,包括限流、一致性、容错等重要概念。
-
适用人群:分布式系统开发者、系统架构师。
3.3 《高并发编程实战》
-
作者:[张开涛]
-
内容简介:这本书结合实际案例,详细介绍了高并发编程的技巧和方法,包括限流、线程池、锁等技术。
-
适用人群:Java开发工程师、系统架构师。
4. 总结
-
固定窗口计数器算法:简单高效,但可能出现突发流量问题。
-
滑动窗口日志算法:精确控制流量,避免突发流量问题。
-
漏桶算法:平滑流量,避免突发流量。
-
令牌桶算法:精确控制流量,支持突发流量。