架构之限流
引言
在现代分布式系统中,流量控制是保障系统稳定性的核心机制之一。当系统面临突发流量时,如果没有有效的限流策略,可能导致服务雪崩、资源耗尽、下游系统过载等严重后果。
限流法则强调:高可用系统必须具备流量控制能力,通过计数器、漏桶、令牌桶等限流算法,在系统容量边界内平滑处理请求,保护自身系统和下游系统不被巨型流量冲垮,确保核心服务的可用性。
限流架构的核心理念
为什么需要限流?
微博案例:明星恋情公布
场景描述:
某明星在微博公布恋情,引发了巨大的流量冲击:
流量分析:
| 指标 | 平时状态 | 突发状态 | 系统容量 |
|---|---|---|---|
| 访问量 | 50万QPS | 500万QPS | 200万QPS |
| 流量倍数 | 1x | 10x | 4x |
| 处理策略 | 正常处理 | 需要限流 | 限流阈值 |
限流策略:
- 核心接口限流:保证核心功能可用
- 非核心接口降级:降低优先级接口的限流阈值
- 用户维度限流:限制单个用户的请求频率
- 地域维度限流:根据不同地区的流量情况调整限流策略
限流算法体系
计数器算法
固定窗口计数器
核心原理
固定窗口计数器是最简单的限流算法,将时间划分为固定大小的窗口,在每个窗口内统计请求次数,超过阈值则拒绝请求。
代码实现
/**
* 固定窗口计数器限流器
*/
@Component
public class FixedWindowRateLimiter {
private static final Logger log = LoggerFactory.getLogger(FixedWindowRateLimiter.class);
private final long windowSizeInMillis; // 窗口大小(毫秒)
private final int maxRequests; // 最大请求数
private final AtomicLong counter; // 请求计数器
private final AtomicLong windowStart; // 窗口开始时间
/**
* 构造函数
* @param windowSizeInSeconds 窗口大小(秒)
* @param maxRequests 最大请求数
*/
public FixedWindowRateLimiter(int windowSizeInSeconds, int maxRequests) {
this.windowSizeInMillis = windowSizeInSeconds * 1000L;
this.maxRequests = maxRequests;
this.counter = new AtomicLong(0);
this.windowStart = new AtomicLong(System.currentTimeMillis());
log.info("初始化固定窗口限流器: 窗口大小={}秒, 最大请求={}",
windowSizeInSeconds, maxRequests);
}
/**
* 尝试获取访问许可
* @return true表示允许访问,false表示被限流
*/
public boolean tryAcquire() {
long currentTime = System.currentTimeMillis();
long currentWindowStart = windowStart.get();
// 检查是否需要重置窗口
if (currentTime - currentWindowStart >= windowSizeInMillis) {
// 使用CAS操作重置窗口
if (windowStart.compareAndSet(currentWindowStart, currentTime)) {
counter.set(0);
log.debug("重置计数器窗口: {}", currentTime);
}
}
// 增加计数
long currentCount = counter.incrementAndGet();
if (currentCount <= maxRequests) {
log.debug("请求允许: 当前计数={}, 最大请求={}", currentCount, maxRequests);
return true;
} else {
// 超过限制,回滚计数
counter.decrementAndGet();
log.warn("请求被限流: 当前计数={}, 最大请求={}", currentCount, maxRequests);
return false;
}
}
/**
* 获取当前窗口的请求计数
*/
public long getCurrentCount() {
return counter.get();
}
/**
* 获取剩余可用请求数
*/
public int getAvailablePermits() {
return Math.max(0, maxRequests - (int) counter.get());
}
/**
* 性能测试
*/
public void performanceTest() {
log.info("=== 固定窗口计数器性能测试 ===");
FixedWindowRateLimiter limiter = new FixedWindowRateLimiter(1, 1000);
int totalRequests = 10000;
int allowedCount = 0;
int rejectedCount = 0;
long startTime = System.currentTimeMillis();
for (int i = 0; i < totalRequests; i++) {
if (limiter.tryAcquire()) {
allowedCount++;
} else {
rejectedCount++;
}
// 模拟请求间隔
try {
Thread.sleep(0, 100000); // 0.1ms
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
long endTime = System.currentTimeMillis();
log.info("总请求数: {}, 允许: {}, 拒绝: {}, 耗时: {}ms",
totalRequests, allowedCount, rejectedCount, endTime - startTime);
}
}
/**
* 固定窗口计数器限流器(使用ConcurrentHashMap支持多维度限流)
*/
@Component
public class MultiKeyFixedWindowRateLimiter {
private static final Logger log = LoggerFactory.getLogger(MultiKeyFixedWindowRateLimiter.class);
private final long windowSizeInMillis;
private final int maxRequests;
private final ConcurrentHashMap<String, WindowCounter> counters;
/**
* 窗口计数器内部类
*/
private static class WindowCounter {
private final AtomicLong count;
private final AtomicLong windowStart;
public WindowCounter(long windowStart) {
this.count = new AtomicLong(0);
this.windowStart = new AtomicLong(windowStart);
}
}
public MultiKeyFixedWindowRateLimiter(int windowSizeInSeconds, int maxRequests) {
this.windowSizeInMillis = windowSizeInSeconds * 1000L;
this.maxRequests = maxRequests;
this.counters = new ConcurrentHashMap<>();
log.info("初始化多维度固定窗口限流器: 窗口大小={}秒, 最大请求={}",
windowSizeInSeconds, maxRequests);
}
/**
* 尝试获取访问许可
* @param key 限流维度(如用户ID、IP地址等)
* @return true表示允许访问,false表示被限流
*/
public boolean tryAcquire(String key) {
long currentTime = System.currentTimeMillis();
// 获取或创建计数器
WindowCounter counter = counters.computeIfAbsent(key,
k -> new WindowCounter(currentTime));
long currentWindowStart = counter.windowStart.get();
// 检查是否需要重置窗口
if (currentTime - currentWindowStart >= windowSizeInMillis) {
if (counter.windowStart.compareAndSet(currentWindowStart, currentTime)) {
counter.count.set(0);
log.debug("重置计数器窗口: key={}, 时间={}", key, currentTime);
}
}
// 增加计数
long currentCount = counter.count.incrementAndGet();
if (currentCount <= maxRequests) {
return true;
} else {
counter.count.decrementAndGet();
log.warn("请求被限流: key={}, 当前计数={}", key, currentCount);
return false;
}
}
/**
* 清理过期的计数器
*/
public void cleanupExpiredCounters() {
long currentTime = System.currentTimeMillis();
long expireTime = currentTime - windowSizeInMillis * 2;
counters.entrySet().removeIf(entry -> {
WindowCounter counter = entry.getValue();
return counter.windowStart.get() < expireTime;
});
log.debug("清理过期计数器完成, 当前计数器数量: {}", counters.size());
}
}
边界问题分析
固定窗口计数器存在临界突变问题:
问题说明:
- 假设限流阈值为1000请求/秒
- 窗口1的最后100ms来了900个请求
- 窗口2的前100ms来了900个请求
- 在200ms内实际通过了1800个请求,远超限流阈值
滑动窗口计数器
核心原理
滑动窗口计数器通过将时间窗口划分为更细的粒度,解决了固定窗口的临界突变问题。
代码实现
/**
* 滑动窗口计数器限流器
*/
@Component
public class SlidingWindowRateLimiter {
private static final Logger log = LoggerFactory.getLogger(SlidingWindowRateLimiter.class);
private final long windowSizeInMillis; // 窗口总大小
private final int subWindowCount; // 子窗口数量
private final long subWindowSizeInMillis; // 子窗口大小
private final int maxRequests; // 最大请求数
private final AtomicReferenceArray<SubWindow> subWindows;
private final AtomicInteger currentIndex;
/**
* 子窗口内部类
*/
private static class SubWindow {
private final AtomicLong count;
private final AtomicLong timestamp;
public SubWindow(long timestamp) {
this.count = new AtomicLong(0);
this.timestamp = new AtomicLong(timestamp);
}
public void reset(long newTimestamp) {
count.set(0);
timestamp.set(newTimestamp);
}
}
/**
* 构造函数
* @param windowSizeInSeconds 窗口大小(秒)
* @param subWindowCount 子窗口数量
* @param maxRequests 最大请求数
*/
public SlidingWindowRateLimiter(int windowSizeInSeconds, int subWindowCount, int maxRequests) {
this.windowSizeInMillis = windowSizeInSeconds * 1000L;
this.subWindowCount = subWindowCount;
this.subWindowSizeInMillis = windowSizeInMillis / subWindowCount;
this.maxRequests = maxRequests;
// 初始化子窗口
this.subWindows = new AtomicReferenceArray<>(subWindowCount);
long currentTime = System.currentTimeMillis();
for (int i = 0; i < subWindowCount; i++) {
subWindows.set(i, new SubWindow(currentTime - i * subWindowSizeInMillis));
}
this.currentIndex = new AtomicInteger(0);
log.info("初始化滑动窗口限流器: 窗口大小={}秒, 子窗口数量={}, 最大请求={}",
windowSizeInSeconds, subWindowCount, maxRequests);
}
/**
* 尝试获取访问许可
*/
public boolean tryAcquire() {
long currentTime = System.currentTimeMillis();
// 计算当前子窗口索引
int index = (int) ((currentTime / subWindowSizeInMillis) % subWindowCount);
int oldIndex = currentIndex.get();
// 检查是否需要切换到新的子窗口
if (index != oldIndex) {
if (currentIndex.compareAndSet(oldIndex, index)) {
SubWindow window = subWindows.get(index);
long windowTimestamp = window.timestamp.get();
// 检查子窗口是否过期
if (currentTime - windowTimestamp >= windowSizeInMillis) {
window.reset(currentTime);
}
}
}
// 增加当前子窗口的计数
SubWindow currentWindow = subWindows.get(index);
long currentCount = currentWindow.count.incrementAndGet();
// 计算滑动窗口内的总请求数
long totalCount = calculateTotalCount(currentTime);
if (totalCount <= maxRequests) {
log.debug("请求允许: 当前计数={}, 滑动窗口计数={}", currentCount, totalCount);
return true;
} else {
// 超过限制,回滚计数
currentWindow.count.decrementAndGet();
log.warn("请求被限流: 滑动窗口计数={}, 最大请求={}", totalCount, maxRequests);
return false;
}
}
/**
* 计算滑动窗口内的总请求数
*/
private long calculateTotalCount(long currentTime) {
long totalCount = 0;
long windowStart = currentTime - windowSizeInMillis;
for (int i = 0; i < subWindowCount; i++) {
SubWindow window = subWindows.get(i);
long windowTimestamp = window.timestamp.get();
// 只统计滑动窗口内的子窗口
if (windowTimestamp > windowStart) {
totalCount += window.count.get();
}
}
return totalCount;
}
/**
* 获取当前窗口的请求计数
*/
public long getCurrentCount() {
return calculateTotalCount(System.currentTimeMillis());
}
/**
* 获取剩余可用请求数
*/
public int getAvailablePermits() {
return Math.max(0, maxRequests - (int) getCurrentCount());
}
/**
* 性能测试
*/
public void performanceTest() {
log.info("=== 滑动窗口计数器性能测试 ===");
SlidingWindowRateLimiter limiter = new SlidingWindowRateLimiter(1, 10, 1000);
int totalRequests = 10000;
int allowedCount = 0;
int rejectedCount = 0;
long startTime = System.currentTimeMillis();
for (int i = 0; i < totalRequests; i++) {
if (limiter.tryAcquire()) {
allowedCount++;
} else {
rejectedCount++;
}
// 模拟请求间隔
try {
Thread.sleep(0, 100000); // 0.1ms
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
long endTime = System.currentTimeMillis();
log.info("总请求数: {}, 允许: {}, 拒绝: {}, 耗时: {}ms",
totalRequests, allowedCount, rejectedCount, endTime - startTime);
}
}
计数器算法适用场景
场景1:API网关限流
应用场景:
- 保护后端服务不被过载
- 防止恶意攻击
- 公平分配资源
实现特点:
- 使用滑动窗口算法
- 支持多维度限流(IP、用户、API)
- 可配置不同接口的限流阈值
/**
* API网关限流器
*/
@Component
public class ApiGatewayRateLimiter {
private static final Logger log = LoggerFactory.getLogger(ApiGatewayRateLimiter.class);
private final Map<String, SlidingWindowRateLimiter> apiLimiters;
private final SlidingWindowRateLimiter globalLimiter;
public ApiGatewayRateLimiter() {
this.apiLimiters = new ConcurrentHashMap<>();
// 全局限流:10000 QPS
this.globalLimiter = new SlidingWindowRateLimiter(1, 10, 10000);
// 各API限流配置
apiLimiters.put("/api/user/login", new SlidingWindowRateLimiter(1, 10, 1000));
apiLimiters.put("/api/order/create", new SlidingWindowRateLimiter(1, 10, 500));
apiLimiters.put("/api/product/list", new SlidingWindowRateLimiter(1, 10, 2000));
log.info("API网关限流器初始化完成");
}
/**
* 检查请求是否允许通过
*/
public boolean allowRequest(String apiPath, String userId) {
// 1. 检查全局限流
if (!globalLimiter.tryAcquire()) {
log.warn("全局限流触发: api={}", apiPath);
return false;
}
// 2. 检查API级别限流
SlidingWindowRateLimiter apiLimiter = apiLimiters.get(apiPath);
if (apiLimiter != null && !apiLimiter.tryAcquire()) {
log.warn("API限流触发: api={}", apiPath);
return false;
}
// 3. 检查用户级别限流
String userKey = "user:" + userId;
// 这里可以添加用户级别限流逻辑
return true;
}
}
场景2:用户行为限流
应用场景:
- 防止用户恶意刷接口
- 保护系统资源
- 提供公平的服务
实现特点:
- 基于用户ID限流
- 不同用户不同限流策略
- VIP用户更高限流阈值
/**
* 用户行为限流器
*/
@Component
public class UserBehaviorRateLimiter {
private static final Logger log = LoggerFactory.getLogger(UserBehaviorRateLimiter.class);
private final ConcurrentHashMap<String, SlidingWindowRateLimiter> userLimiters;
private final SlidingWindowRateLimiter defaultLimiter;
private final SlidingWindowRateLimiter vipLimiter;
public UserBehaviorRateLimiter() {
this.userLimiters = new ConcurrentHashMap<>();
// 普通用户:100 QPS
this.defaultLimiter = new SlidingWindowRateLimiter(1, 10, 100);
// VIP用户:500 QPS
this.vipLimiter = new SlidingWindowRateLimiter(1, 10, 500);
log.info("用户行为限流器初始化完成");
}
/**
* 检查用户请求是否允许
*/
public boolean allowUserRequest(String userId, boolean isVip) {
SlidingWindowRateLimiter limiter;
if (isVip) {
limiter = vipLimiter;
} else {
limiter = defaultLimiter;
}
boolean allowed = limiter.tryAcquire();
if (!allowed) {
log.warn("用户限流触发: userId={}, isVip={}", userId, isVip);
}
return allowed;
}
}
计数器算法性能对比
| 算法类型 | 精确度 | 实现复杂度 | 内存占用 | 适用场景 |
|---|---|---|---|---|
| 固定窗口 | 低(临界突变) | 简单 | 低 | 对精确度要求不高的场景 |
| 滑动窗口 | 高 | 中等 | 中等 | 需要精确限流的场景 |
| 多维度限流 | 高 | 较高 | 较高 | 需要多维度控制的场景 |
漏桶算法
核心原理
漏桶算法将请求看作水滴,漏桶看作一个固定容量的桶,水滴以任意速率流入桶中,桶以恒定速率向外滴水。如果桶满了,多余的水滴会溢出(被拒绝)。
漏桶特性
代码实现
/**
* 漏桶限流器
*/
@Component
public class LeakyBucketRateLimiter {
private static final Logger log = LoggerFactory.getLogger(LeakyBucketRateLimiter.class);
private final long capacity; // 桶容量
private final long leakRateInMillis; // 漏水速率(每个请求间隔毫秒数)
private final BlockingQueue<Request> bucket; // 桶(队列)
private final AtomicLong lastLeakTime; // 上次漏水时间
private final ScheduledExecutorService leakExecutor; // 漏水定时器
/**
* 请求内部类
*/
private static class Request {
private final long timestamp;
private final CompletableFuture<Boolean> future;
public Request() {
this.timestamp = System.currentTimeMillis();
this.future = new CompletableFuture<>();
}
}
/**
* 构造函数
* @param capacity 桶容量
* @param leakRatePerSecond 漏水速率(每秒请求数)
*/
public LeakyBucketRateLimiter(int capacity, int leakRatePerSecond) {
this.capacity = capacity;
this.leakRateInMillis = 1000L / leakRatePerSecond;
this.bucket = new LinkedBlockingQueue<>(capacity);
this.lastLeakTime = new AtomicLong(System.currentTimeMillis());
// 启动漏水定时器
this.leakExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
Thread thread = new Thread(r, "leaky-bucket-leak-thread");
thread.setDaemon(true);
return thread;
});
// 启动漏水任务
this.leakExecutor.scheduleAtFixedRate(
this::leak,
leakRateInMillis,
leakRateInMillis,
TimeUnit.MILLISECONDS
);
log.info("初始化漏桶限流器: 容量={}, 漏水速率={}/秒", capacity, leakRatePerSecond);
}
/**
* 漏水操作
*/
private void leak() {
Request request = bucket.poll();
if (request != null) {
// 请求通过
request.future.complete(true);
log.debug("请求通过: 等待时间={}ms",
System.currentTimeMillis() - request.timestamp);
}
}
/**
* 尝试获取访问许可(异步)
*/
public CompletableFuture<Boolean> tryAcquireAsync() {
Request request = new Request();
// 尝试将请求放入桶中
if (bucket.offer(request)) {
return request.future;
} else {
// 桶已满,请求被拒绝
log.warn("请求被拒绝: 桶已满");
return CompletableFuture.completedFuture(false);
}
}
/**
* 尝试获取访问许可(同步)
*/
public boolean tryAcquire() {
Request request = new Request();
// 尝试将请求放入桶中
if (bucket.offer(request)) {
try {
// 等待请求处理完成
return request.future.get(5, TimeUnit.SECONDS);
} catch (Exception e) {
log.error("等待请求处理异常", e);
return false;
}
} else {
// 桶已满,请求被拒绝
log.warn("请求被拒绝: 桶已满");
return false;
}
}
/**
* 获取当前桶中的请求数量
*/
public int getCurrentSize() {
return bucket.size();
}
/**
* 获取剩余容量
*/
public int getRemainingCapacity() {
return bucket.remainingCapacity();
}
/**
* 关闭限流器
*/
public void shutdown() {
leakExecutor.shutdown();
try {
if (!leakExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
leakExecutor.shutdownNow();
}
} catch (InterruptedException e) {
leakExecutor.shutdownNow();
Thread.currentThread().interrupt();
}
log.info("漏桶限流器已关闭");
}
/**
* 性能测试
*/
public void performanceTest() {
log.info("=== 漏桶算法性能测试 ===");
LeakyBucketRateLimiter limiter = new LeakyBucketRateLimiter(100, 100);
int totalRequests = 1000;
int allowedCount = 0;
int rejectedCount = 0;
long startTime = System.currentTimeMillis();
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < totalRequests; i++) {
final int requestNum = i;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
boolean allowed = limiter.tryAcquire();
synchronized (LeakyBucketRateLimiter.this) {
if (allowed) {
allowedCount++;
} else {
rejectedCount++;
}
}
});
futures.add(future);
}
// 等待所有请求完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
long endTime = System.currentTimeMillis();
log.info("总请求数: {}, 允许: {}, 拒绝: {}, 耗时: {}ms",
totalRequests, allowedCount, rejectedCount, endTime - startTime);
limiter.shutdown();
}
}
/**
* 分布式漏桶限流器(基于Redis)
*/
@Component
public class DistributedLeakyBucketRateLimiter {
private static final Logger log = LoggerFactory.getLogger(DistributedLeakyBucketRateLimiter.class);
private final StringRedisTemplate redisTemplate;
private final long capacity;
private final long leakRateInMillis;
private static final String BUCKET_PREFIX = "leaky:bucket:";
private static final String BUCKET_SIZE_SUFFIX = ":size";
private static final String BUCKET_LAST_LEAK_SUFFIX = ":last_leak";
public DistributedLeakyBucketRateLimiter(StringRedisTemplate redisTemplate,
int capacity, int leakRatePerSecond) {
this.redisTemplate = redisTemplate;
this.capacity = capacity;
this.leakRateInMillis = 1000L / leakRatePerSecond;
log.info("初始化分布式漏桶限流器: 容量={}, 漏水速率={}/秒", capacity, leakRatePerSecond);
}
/**
* 尝试获取访问许可
* @param key 限流键
* @return true表示允许,false表示被拒绝
*/
public boolean tryAcquire(String key) {
String sizeKey = BUCKET_PREFIX + key + BUCKET_SIZE_SUFFIX;
String lastLeakKey = BUCKET_PREFIX + key + BUCKET_LAST_LEAK_SUFFIX;
long currentTime = System.currentTimeMillis();
// 使用Lua脚本保证原子性
String luaScript =
"local sizeKey = KEYS[1]\n" +
"local lastLeakKey = KEYS[2]\n" +
"local capacity = tonumber(ARGV[1])\n" +
"local leakRate = tonumber(ARGV[2])\n" +
"local currentTime = tonumber(ARGV[3])\n" +
"\n" +
"local size = tonumber(redis.call('GET', sizeKey) or 0)\n" +
"local lastLeakTime = tonumber(redis.call('GET', lastLeakKey) or currentTime)\n" +
"\n" +
"-- 计算应该漏掉的数量\n" +
"local elapsedTime = currentTime - lastLeakTime\n" +
"local leaked = math.floor(elapsedTime / leakRate)\n" +
"\n" +
"-- 更新桶大小\n" +
"size = math.max(0, size - leaked)\n" +
"redis.call('SET', lastLeakKey, currentTime)\n" +
"\n" +
"-- 检查是否可以添加请求\n" +
"if size < capacity then\n" +
" size = size + 1\n" +
" redis.call('SET', sizeKey, size)\n" +
" return 1\n" +
"else\n" +
" return 0\n" +
"end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
Long result = redisTemplate.execute(redisScript,
Arrays.asList(sizeKey, lastLeakKey),
capacity, leakRateInMillis, currentTime);
boolean allowed = result != null && result == 1;
if (!allowed) {
log.warn("分布式漏桶限流: key={}", key);
}
return allowed;
}
/**
* 获取当前桶大小
*/
public long getCurrentSize(String key) {
String sizeKey = BUCKET_PREFIX + key + BUCKET_SIZE_SUFFIX;
String sizeStr = redisTemplate.opsForValue().get(sizeKey);
return sizeStr != null ? Long.parseLong(sizeStr) : 0;
}
}
漏桶算法适用场景
场景1:数据库连接池限流
应用场景:
- 保护数据库不被过载
- 平滑数据库访问流量
- 控制并发连接数
/**
* 数据库连接池限流器
*/
@Component
public class DatabaseConnectionRateLimiter {
private static final Logger log = LoggerFactory.getLogger(DatabaseConnectionRateLimiter.class);
private final LeakyBucketRateLimiter connectionLimiter;
public DatabaseConnectionRateLimiter() {
// 桶容量:最大连接数
// 漏水速率:每秒处理的最大连接数
this.connectionLimiter = new LeakyBucketRateLimiter(100, 50);
log.info("数据库连接池限流器初始化完成");
}
/**
* 获取数据库连接
*/
public Connection getConnection() throws SQLException {
// 等待获取连接许可
if (connectionLimiter.tryAcquire()) {
// 获取实际连接
return getActualConnection();
} else {
throw new SQLException("连接池已满,请稍后重试");
}
}
private Connection getActualConnection() {
// 实际获取数据库连接的逻辑
return null;
}
}
场景2:消息队列消费者限流
应用场景:
- 控制消息消费速率
- 保护下游处理系统
- 平滑消息处理流量
/**
* 消息队列消费者限流器
*/
@Component
public class MessageConsumerRateLimiter {
private static final Logger log = LoggerFactory.getLogger(MessageConsumerRateLimiter.class);
private final LeakyBucketRateLimiter consumerLimiter;
public MessageConsumerRateLimiter() {
// 桶容量:缓冲队列大小
// 漏水速率:每秒处理的消息数
this.consumerLimiter = new LeakyBucketRateLimiter(1000, 100);
log.info("消息队列消费者限流器初始化完成");
}
/**
* 处理消息
*/
public void processMessage(Message message) {
// 异步处理消息
CompletableFuture.runAsync(() -> {
try {
// 等待处理许可
if (consumerLimiter.tryAcquire()) {
handleMessage(message);
} else {
log.warn("消息处理限流: messageId={}", message.getId());
// 可以将消息重新放入队列或发送到死信队列
}
} catch (Exception e) {
log.error("处理消息异常", e);
}
});
}
private void handleMessage(Message message) {
// 实际处理消息的逻辑
}
}
漏桶算法优缺点
| 优点 | 缺点 |
|---|---|
| 流量平滑,输出速率恒定 | 无法处理突发流量 |
| 削峰填谷,保护下游系统 | 突发流量会被丢弃 |
| 实现相对简单 | 需要维护队列,占用内存 |
| 适合保护有处理能力限制的系统 | 不适合需要快速响应的场景 |
令牌桶算法
核心原理
令牌桶算法以恒定速率向桶中放入令牌,请求到达时从桶中获取令牌,如果桶中有令牌则允许请求通过,否则拒绝请求。令牌桶可以积累一定数量的令牌,因此可以处理突发流量。
令牌桶特性
代码实现
/**
* 令牌桶限流器
*/
@Component
public class TokenBucketRateLimiter {
private static final Logger log = LoggerFactory.getLogger(TokenBucketRateLimiter.class);
private final long capacity; // 桶容量
private final long refillRateInMillis; // 令牌补充速率(每个令牌间隔毫秒数)
private final AtomicLong tokens; // 当前令牌数
private final AtomicLong lastRefillTime; // 上次补充令牌时间
/**
* 构造函数
* @param capacity 桶容量
* @param refillRatePerSecond 令牌补充速率(每秒令牌数)
*/
public TokenBucketRateLimiter(long capacity, long refillRatePerSecond) {
this.capacity = capacity;
this.refillRateInMillis = 1000L / refillRatePerSecond;
this.tokens = new AtomicLong(capacity);
this.lastRefillTime = new AtomicLong(System.currentTimeMillis());
log.info("初始化令牌桶限流器: 容量={}, 补充速率={}/秒", capacity, refillRatePerSecond);
}
/**
* 补充令牌
*/
private void refillTokens() {
long currentTime = System.currentTimeMillis();
long lastRefill = lastRefillTime.get();
long elapsedTime = currentTime - lastRefill;
if (elapsedTime >= refillRateInMillis) {
// 计算应该补充的令牌数
long tokensToAdd = elapsedTime / refillRateInMillis;
// 使用CAS操作更新令牌数
long currentTokens = tokens.get();
long newTokens = Math.min(capacity, currentTokens + tokensToAdd);
if (tokens.compareAndSet(currentTokens, newTokens)) {
lastRefillTime.set(currentTime - (elapsedTime % refillRateInMillis));
log.debug("补充令牌: 新增={}, 当前={}", tokensToAdd, newTokens);
}
}
}
/**
* 尝试获取访问许可
* @return true表示允许,false表示被拒绝
*/
public boolean tryAcquire() {
refillTokens();
// 尝试获取令牌
while (true) {
long currentTokens = tokens.get();
if (currentTokens > 0) {
if (tokens.compareAndSet(currentTokens, currentTokens - 1)) {
log.debug("获取令牌成功: 剩余令牌={}", currentTokens - 1);
return true;
}
} else {
log.warn("获取令牌失败: 桶已空");
return false;
}
}
}
/**
* 尝试获取多个访问许可
* @param permits 需要的许可数
* @return true表示允许,false表示被拒绝
*/
public boolean tryAcquire(int permits) {
if (permits <= 0) {
return true;
}
refillTokens();
// 尝试获取多个令牌
while (true) {
long currentTokens = tokens.get();
if (currentTokens >= permits) {
if (tokens.compareAndSet(currentTokens, currentTokens - permits)) {
log.debug("获取令牌成功: 获取={}, 剩余={}", permits, currentTokens - permits);
return true;
}
} else {
log.warn("获取令牌失败: 需要={}, 当前={}", permits, currentTokens);
return false;
}
}
}
/**
* 阻塞获取访问许可
*/
public void acquire() {
while (!tryAcquire()) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("获取令牌被中断", e);
}
}
}
/**
* 带超时的阻塞获取访问许可
* @param timeout 超时时间(毫秒)
* @return true表示获取成功,false表示超时
*/
public boolean tryAcquire(long timeout) {
long endTime = System.currentTimeMillis() + timeout;
while (System.currentTimeMillis() < endTime) {
if (tryAcquire()) {
return true;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("获取令牌被中断", e);
}
}
return false;
}
/**
* 获取当前令牌数
*/
public long getCurrentTokens() {
refillTokens();
return tokens.get();
}
/**
* 性能测试
*/
public void performanceTest() {
log.info("=== 令牌桶算法性能测试 ===");
TokenBucketRateLimiter limiter = new TokenBucketRateLimiter(100, 100);
int totalRequests = 1000;
int allowedCount = 0;
int rejectedCount = 0;
long startTime = System.currentTimeMillis();
for (int i = 0; i < totalRequests; i++) {
if (limiter.tryAcquire()) {
allowedCount++;
} else {
rejectedCount++;
}
// 模拟请求间隔
try {
Thread.sleep(0, 100000); // 0.1ms
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
long endTime = System.currentTimeMillis();
log.info("总请求数: {}, 允许: {}, 拒绝: {}, 耗时: {}ms",
totalRequests, allowedCount, rejectedCount, endTime - startTime);
}
}
/**
* 分布式令牌桶限流器(基于Redis)
*/
@Component
public class DistributedTokenBucketRateLimiter {
private static final Logger log = LoggerFactory.getLogger(DistributedTokenBucketRateLimiter.class);
private final StringRedisTemplate redisTemplate;
private final long capacity;
private final long refillRateInMillis;
private static final String BUCKET_PREFIX = "token:bucket:";
private static final String BUCKET_TOKENS_SUFFIX = ":tokens";
private static final String BUCKET_LAST_REFILL_SUFFIX = ":last_refill";
public DistributedTokenBucketRateLimiter(StringRedisTemplate redisTemplate,
long capacity, long refillRatePerSecond) {
this.redisTemplate = redisTemplate;
this.capacity = capacity;
this.refillRateInMillis = 1000L / refillRatePerSecond;
log.info("初始化分布式令牌桶限流器: 容量={}, 补充速率={}/秒", capacity, refillRatePerSecond);
}
/**
* 尝试获取访问许可
* @param key 限流键
* @return true表示允许,false表示被拒绝
*/
public boolean tryAcquire(String key) {
return tryAcquire(key, 1);
}
/**
* 尝试获取多个访问许可
* @param key 限流键
* @param permits 需要的许可数
* @return true表示允许,false表示被拒绝
*/
public boolean tryAcquire(String key, int permits) {
String tokensKey = BUCKET_PREFIX + key + BUCKET_TOKENS_SUFFIX;
String lastRefillKey = BUCKET_PREFIX + key + BUCKET_LAST_REFILL_SUFFIX;
long currentTime = System.currentTimeMillis();
// 使用Lua脚本保证原子性
String luaScript =
"local tokensKey = KEYS[1]\n" +
"local lastRefillKey = KEYS[2]\n" +
"local capacity = tonumber(ARGV[1])\n" +
"local refillRate = tonumber(ARGV[2])\n" +
"local permits = tonumber(ARGV[3])\n" +
"local currentTime = tonumber(ARGV[4])\n" +
"\n" +
"local tokens = tonumber(redis.call('GET', tokensKey) or capacity)\n" +
"local lastRefillTime = tonumber(redis.call('GET', lastRefillKey) or currentTime)\n" +
"\n" +
"-- 计算应该补充的令牌数\n" +
"local elapsedTime = currentTime - lastRefillTime\n" +
"local tokensToAdd = math.floor(elapsedTime / refillRate)\n" +
"\n" +
"-- 更新令牌数\n" +
"tokens = math.min(capacity, tokens + tokensToAdd)\n" +
"redis.call('SET', lastRefillKey, currentTime)\n" +
"\n" +
"-- 检查是否可以获取令牌\n" +
"if tokens >= permits then\n" +
" tokens = tokens - permits\n" +
" redis.call('SET', tokensKey, tokens)\n" +
" return 1\n" +
"else\n" +
" redis.call('SET', tokensKey, tokens)\n" +
" return 0\n" +
"end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
Long result = redisTemplate.execute(redisScript,
Arrays.asList(tokensKey, lastRefillKey),
capacity, refillRateInMillis, permits, currentTime);
boolean allowed = result != null && result == 1;
if (!allowed) {
log.warn("分布式令牌桶限流: key={}, permits={}", key, permits);
}
return allowed;
}
/**
* 获取当前令牌数
*/
public long getCurrentTokens(String key) {
String tokensKey = BUCKET_PREFIX + key + BUCKET_TOKENS_SUFFIX;
String tokensStr = redisTemplate.opsForValue().get(tokensKey);
return tokensStr != null ? Long.parseLong(tokensStr) : capacity;
}
}
令牌桶算法适用场景
场景1:API接口限流
应用场景:
- 保护API不被过载
- 支持突发流量
- 灵活配置不同接口的限流策略
/**
* API接口令牌桶限流器
*/
@Component
public class ApiTokenBucketRateLimiter {
private static final Logger log = LoggerFactory.getLogger(ApiTokenBucketRateLimiter.class);
private final Map<String, TokenBucketRateLimiter> apiLimiters;
private final TokenBucketRateLimiter globalLimiter;
public ApiTokenBucketRateLimiter() {
this.apiLimiters = new ConcurrentHashMap<>();
// 全局限流:10000 QPS,桶容量20000(支持突发)
this.globalLimiter = new TokenBucketRateLimiter(20000, 10000);
// 各API限流配置
apiLimiters.put("/api/user/login", new TokenBucketRateLimiter(2000, 1000));
apiLimiters.put("/api/order/create", new TokenBucketRateLimiter(1000, 500));
apiLimiters.put("/api/product/list", new TokenBucketRateLimiter(4000, 2000));
log.info("API令牌桶限流器初始化完成");
}
/**
* 检查请求是否允许通过
*/
public boolean allowRequest(String apiPath, String userId) {
// 1. 检查全局限流
if (!globalLimiter.tryAcquire()) {
log.warn("全局限流触发: api={}", apiPath);
return false;
}
// 2. 检查API级别限流
TokenBucketRateLimiter apiLimiter = apiLimiters.get(apiPath);
if (apiLimiter != null && !apiLimiter.tryAcquire()) {
log.warn("API限流触发: api={}", apiPath);
return false;
}
// 3. 检查用户级别限流
String userKey = "user:" + userId;
// 这里可以添加用户级别限流逻辑
return true;
}
/**
* 批量请求处理(支持突发)
*/
public boolean allowBatchRequest(String apiPath, int batchSize) {
TokenBucketRateLimiter apiLimiter = apiLimiters.get(apiPath);
if (apiLimiter != null) {
return apiLimiter.tryAcquire(batchSize);
}
return true;
}
}
场景2:微服务间调用限流
应用场景:
- 保护下游微服务
- 控制调用速率
- 支持批量调用
/**
* 微服务调用令牌桶限流器
*/
@Component
public class MicroserviceCallRateLimiter {
private static final Logger log = LoggerFactory.getLogger(MicroserviceCallRateLimiter.class);
private final Map<String, TokenBucketRateLimiter> serviceLimiters;
public MicroserviceCallRateLimiter() {
this.serviceLimiters = new ConcurrentHashMap<>();
// 各微服务限流配置
serviceLimiters.put("user-service", new TokenBucketRateLimiter(5000, 2000));
serviceLimiters.put("order-service", new TokenBucketRateLimiter(3000, 1000));
serviceLimiters.put("product-service", new TokenBucketRateLimiter(8000, 3000));
log.info("微服务调用限流器初始化完成");
}
/**
* 调用微服务
*/
public <T> T callService(String serviceName, Supplier<T> callable) {
TokenBucketRateLimiter limiter = serviceLimiters.get(serviceName);
if (limiter != null && !limiter.tryAcquire()) {
log.warn("微服务调用限流: service={}", serviceName);
throw new RateLimitException("服务调用频率超限,请稍后重试");
}
try {
return callable.get();
} catch (Exception e) {
log.error("微服务调用异常: service={}", serviceName, e);
throw new ServiceException("服务调用失败", e);
}
}
/**
* 批量调用微服务
*/
public <T> List<T> callServiceBatch(String serviceName, List<Supplier<T>> callables) {
TokenBucketRateLimiter limiter = serviceLimiters.get(serviceName);
if (limiter != null && !limiter.tryAcquire(callables.size())) {
log.warn("微服务批量调用限流: service={}, size={}", serviceName, callables.size());
throw new RateLimitException("批量服务调用频率超限");
}
List<T> results = new ArrayList<>();
for (Supplier<T> callable : callables) {
try {
results.add(callable.get());
} catch (Exception e) {
log.error("微服务批量调用异常: service={}", serviceName, e);
}
}
return results;
}
}
令牌桶算法优缺点
| 优点 | 缺点 |
|---|---|
| 支持突发流量 | 实现相对复杂 |
| 灵活配置生成速率和容量 | 需要维护令牌计数 |
| 平滑限流,长期平均速率恒定 | 突发流量后令牌需要时间恢复 |
| 适合需要处理突发流量的场景 | 短期内可能超过限流阈值 |
三种算法对比
算法特性对比
详细对比表
| 对比维度 | 固定窗口计数器 | 滑动窗口计数器 | 漏桶算法 | 令牌桶算法 |
|---|---|---|---|---|
| 实现复杂度 | 简单 | 中等 | 中等 | 中等 |
| 限流精确度 | 低(临界突变) | 高 | 高 | 高 |
| 突发流量处理 | 不支持 | 不支持 | 不支持 | 支持 |
| 流量平滑性 | 差 | 一般 | 好 | 好 |
| 内存占用 | 低 | 中等 | 较高(队列) | 低 |
| 适用场景 | 简单限流 | 精确限流 | 保护下游系统 | 需要处理突发流量 |
| 资源消耗 | 低 | 中等 | 较高 | 中等 |
| 长期平均速率 | 可控 | 可控 | 恒定 | 可控 |
| 短期突发能力 | 无 | 无 | 无 | 有 |
算法选择决策树
限流架构设计
多层限流架构
分布式限流架构
/**
* 分布式限流管理器
*/
@Component
public class DistributedRateLimitManager {
private static final Logger log = LoggerFactory.getLogger(DistributedRateLimitManager.class);
private final StringRedisTemplate redisTemplate;
private final Map<String, RateLimiterStrategy> strategies;
/**
* 限流策略接口
*/
public interface RateLimiterStrategy {
boolean tryAcquire(String key, int permits);
long getCurrentTokens(String key);
}
/**
* 令牌桶策略
*/
@Component
public static class TokenBucketStrategy implements RateLimiterStrategy {
private final StringRedisTemplate redisTemplate;
private final long capacity;
private final long refillRatePerSecond;
public TokenBucketStrategy(StringRedisTemplate redisTemplate,
long capacity, long refillRatePerSecond) {
this.redisTemplate = redisTemplate;
this.capacity = capacity;
this.refillRatePerSecond = refillRatePerSecond;
}
@Override
public boolean tryAcquire(String key, int permits) {
// 实现令牌桶逻辑
return true;
}
@Override
public long getCurrentTokens(String key) {
// 获取当前令牌数
return 0;
}
}
public DistributedRateLimitManager(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
this.strategies = new ConcurrentHashMap<>();
// 初始化各种限流策略
strategies.put("token-bucket", new TokenBucketStrategy(redisTemplate, 10000, 1000));
log.info("分布式限流管理器初始化完成");
}
/**
* 尝试获取访问许可
*/
public boolean tryAcquire(String strategy, String key, int permits) {
RateLimiterStrategy limiter = strategies.get(strategy);
if (limiter == null) {
log.error("未找到限流策略: {}", strategy);
return false;
}
return limiter.tryAcquire(key, permits);
}
/**
* 获取当前令牌数
*/
public long getCurrentTokens(String strategy, String key) {
RateLimiterStrategy limiter = strategies.get(strategy);
if (limiter == null) {
return 0;
}
return limiter.getCurrentTokens(key);
}
}
限流配置管理
/**
* 限流配置管理器
*/
@Component
public class RateLimitConfigManager {
private static final Logger log = LoggerFactory.getLogger(RateLimitConfigManager.class);
private final Map<String, RateLimitConfig> configs;
/**
* 限流配置类
*/
@Data
@Builder
public static class RateLimitConfig {
private String key; // 限流键
private String strategy; // 限流策略
private long capacity; // 容量
private long rate; // 速率
private String dimension; // 限流维度
private int priority; // 优先级
private boolean enabled; // 是否启用
}
public RateLimitConfigManager() {
this.configs = new ConcurrentHashMap<>();
// 加载默认配置
loadDefaultConfigs();
log.info("限流配置管理器初始化完成");
}
/**
* 加载默认配置
*/
private void loadDefaultConfigs() {
configs.put("global", RateLimitConfig.builder()
.key("global")
.strategy("token-bucket")
.capacity(10000)
.rate(1000)
.dimension("global")
.priority(1)
.enabled(true)
.build());
configs.put("api:user:login", RateLimitConfig.builder()
.key("api:user:login")
.strategy("token-bucket")
.capacity(2000)
.rate(100)
.dimension("api")
.priority(2)
.enabled(true)
.build());
}
/**
* 获取限流配置
*/
public RateLimitConfig getConfig(String key) {
return configs.get(key);
}
/**
* 更新限流配置
*/
public void updateConfig(String key, RateLimitConfig config) {
configs.put(key, config);
log.info("更新限流配置: key={}", key);
}
/**
* 动态调整限流阈值
*/
public void adjustThreshold(String key, long newRate) {
RateLimitConfig config = configs.get(key);
if (config != null) {
config.setRate(newRate);
log.info("调整限流阈值: key={}, newRate={}", key, newRate);
}
}
}
限流最佳实践
实践1:限流策略设计
设计原则
- 分层限流:在不同层级实施限流,形成防护体系
- 多维度限流:从IP、用户、API等多个维度限流
- 动态调整:根据系统负载动态调整限流阈值
- 优雅降级:限流时提供降级服务
/**
* 分层限流策略
*/
@Component
public class LayeredRateLimitStrategy {
private static final Logger log = LoggerFactory.getLogger(LayeredRateLimitStrategy.class);
private final TokenBucketRateLimiter globalLimiter; // 全局限流
private final TokenBucketRateLimiter apiLimiter; // API限流
private final TokenBucketRateLimiter userLimiter; // 用户限流
private final TokenBucketRateLimiter ipLimiter; // IP限流
public LayeredRateLimitStrategy() {
// 全局限流:10000 QPS,桶容量20000
this.globalLimiter = new TokenBucketRateLimiter(20000, 10000);
// API限流:1000 QPS,桶容量2000
this.apiLimiter = new TokenBucketRateLimiter(2000, 1000);
// 用户限流:100 QPS,桶容量200
this.userLimiter = new TokenBucketRateLimiter(200, 100);
// IP限流:50 QPS,桶容量100
this.ipLimiter = new TokenBucketRateLimiter(100, 50);
log.info("分层限流策略初始化完成");
}
/**
* 检查请求是否允许通过
*/
public RateLimitResult checkRequest(RequestContext context) {
// 1. 检查全局限流
if (!globalLimiter.tryAcquire()) {
log.warn("全局限流触发");
return RateLimitResult.rejected("全局限流", "global");
}
// 2. 检查API限流
if (!apiLimiter.tryAcquire()) {
log.warn("API限流触发: api={}", context.getApiPath());
return RateLimitResult.rejected("API限流", "api");
}
// 3. 检查用户限流
if (context.getUserId() != null && !userLimiter.tryAcquire()) {
log.warn("用户限流触发: userId={}", context.getUserId());
return RateLimitResult.rejected("用户限流", "user");
}
// 4. 检查IP限流
if (context.getClientIp() != null && !ipLimiter.tryAcquire()) {
log.warn("IP限流触发: ip={}", context.getClientIp());
return RateLimitResult.rejected("IP限流", "ip");
}
return RateLimitResult.allowed();
}
/**
* 限流结果
*/
@Data
@AllArgsConstructor
public static class RateLimitResult {
private boolean allowed;
private String reason;
private String dimension;
public static RateLimitResult allowed() {
return new RateLimitResult(true, null, null);
}
public static RateLimitResult rejected(String reason, String dimension) {
return new RateLimitResult(false, reason, dimension);
}
}
}
实践2:限流监控与告警
/**
* 限流监控器
*/
@Component
public class RateLimitMonitor {
private static final Logger log = LoggerFactory.getLogger(RateLimitMonitor.class);
private final AtomicLong totalRequests;
private final AtomicLong allowedRequests;
private final AtomicLong rejectedRequests;
private final ConcurrentHashMap<String, AtomicLong> rejectedByDimension;
private final ScheduledExecutorService monitorExecutor;
public RateLimitMonitor() {
this.totalRequests = new AtomicLong(0);
this.allowedRequests = new AtomicLong(0);
this.rejectedRequests = new AtomicLong(0);
this.rejectedByDimension = new ConcurrentHashMap<>();
// 启动监控任务
this.monitorExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
Thread thread = new Thread(r, "rate-limit-monitor");
thread.setDaemon(true);
return thread;
});
this.monitorExecutor.scheduleAtFixedRate(
this::reportMetrics,
1, 1, TimeUnit.MINUTES
);
log.info("限流监控器初始化完成");
}
/**
* 记录请求
*/
public void recordRequest(boolean allowed, String dimension) {
totalRequests.incrementAndGet();
if (allowed) {
allowedRequests.incrementAndGet();
} else {
rejectedRequests.incrementAndGet();
if (dimension != null) {
rejectedByDimension.computeIfAbsent(dimension,
k -> new AtomicLong(0)).incrementAndGet();
}
}
}
/**
* 获取限流统计
*/
public RateLimitStats getStats() {
Map<String, Long> rejectedByDim = new HashMap<>();
rejectedByDimension.forEach((k, v) -> rejectedByDim.put(k, v.get()));
return RateLimitStats.builder()
.totalRequests(totalRequests.get())
.allowedRequests(allowedRequests.get())
.rejectedRequests(rejectedRequests.get())
.rejectedByDimension(rejectedByDim)
.build();
}
/**
* 报告监控指标
*/
private void reportMetrics() {
RateLimitStats stats = getStats();
log.info("=== 限流监控指标 ===");
log.info("总请求数: {}", stats.getTotalRequests());
log.info("允许请求数: {}", stats.getAllowedRequests());
log.info("拒绝请求数: {}", stats.getRejectedRequests());
log.info("拒绝率: {}%",
stats.getTotalRequests() > 0 ?
(stats.getRejectedRequests() * 100 / stats.getTotalRequests()) : 0);
// 检查是否需要告警
if (stats.getRejectedRequests() > 1000) {
log.warn("限流拒绝请求过多,触发告警: {}", stats.getRejectedRequests());
// 发送告警通知
sendAlert(stats);
}
}
/**
* 发送告警
*/
private void sendAlert(RateLimitStats stats) {
// 实现告警逻辑
log.warn("发送限流告警: {}", stats);
}
/**
* 限流统计
*/
@Data
@Builder
public static class RateLimitStats {
private long totalRequests;
private long allowedRequests;
private long rejectedRequests;
private Map<String, Long> rejectedByDimension;
}
}
实践3:限流降级策略
/**
* 限流降级处理器
*/
@Component
public class RateLimitFallbackHandler {
private static final Logger log = LoggerFactory.getLogger(RateLimitFallbackHandler.class);
/**
* 处理限流降级
*/
public Object handleFallback(RequestContext context, String dimension) {
log.info("执行限流降级: dimension={}, api={}", dimension, context.getApiPath());
// 根据不同维度执行不同的降级策略
switch (dimension) {
case "global":
return handleGlobalFallback(context);
case "api":
return handleApiFallback(context);
case "user":
return handleUserFallback(context);
case "ip":
return handleIpFallback(context);
default:
return handleDefaultFallback(context);
}
}
/**
* 全局限流降级
*/
private Object handleGlobalFallback(RequestContext context) {
log.warn("全局限流降级: 返回服务繁忙提示");
// 返回服务繁忙提示
return Response.builder()
.code(503)
.message("服务繁忙,请稍后重试")
.timestamp(System.currentTimeMillis())
.build();
}
/**
* API限流降级
*/
private Object handleApiFallback(RequestContext context) {
log.warn("API限流降级: api={}", context.getApiPath());
// 返回缓存数据或降级数据
return getCachedData(context.getApiPath());
}
/**
* 用户限流降级
*/
private Object handleUserFallback(RequestContext context) {
log.warn("用户限流降级: userId={}", context.getUserId());
// 返回用户限流提示
return Response.builder()
.code(429)
.message("请求过于频繁,请稍后重试")
.timestamp(System.currentTimeMillis())
.build();
}
/**
* IP限流降级
*/
private Object handleIpFallback(RequestContext context) {
log.warn("IP限流降级: ip={}", context.getClientIp());
// 返回验证码或限制提示
return Response.builder()
.code(429)
.message("检测到异常请求,请完成验证")
.timestamp(System.currentTimeMillis())
.build();
}
/**
* 默认降级
*/
private Object handleDefaultFallback(RequestContext context) {
return Response.builder()
.code(503)
.message("服务暂时不可用")
.timestamp(System.currentTimeMillis())
.build();
}
/**
* 获取缓存数据
*/
private Object getCachedData(String apiPath) {
// 从缓存获取数据
return null;
}
}
限流实战案例
案例1:电商秒杀系统限流
场景描述:
- 秒杀活动期间流量激增
- 需要保护系统不被冲垮
- 保证核心功能可用
限流策略:
/**
* 秒杀系统限流器
*/
@Component
public class SeckillRateLimiter {
private static final Logger log = LoggerFactory.getLogger(SeckillRateLimiter.class);
private final TokenBucketRateLimiter globalLimiter;
private final ConcurrentHashMap<String, TokenBucketRateLimiter> productLimiters;
private final ConcurrentHashMap<String, TokenBucketRateLimiter> userLimiters;
public SeckillRateLimiter() {
// 全局限流:50000 QPS,桶容量100000(支持突发)
this.globalLimiter = new TokenBucketRateLimiter(100000, 50000);
this.productLimiters = new ConcurrentHashMap<>();
this.userLimiters = new ConcurrentHashMap<>();
log.info("秒杀系统限流器初始化完成");
}
/**
* 检查秒杀请求是否允许
*/
public boolean allowSeckillRequest(String productId, String userId) {
// 1. 检查全局限流
if (!globalLimiter.tryAcquire()) {
log.warn("秒杀全局限流触发");
return false;
}
// 2. 检查商品维度限流
TokenBucketRateLimiter productLimiter = productLimiters.computeIfAbsent(
productId,
k -> new TokenBucketRateLimiter(5000, 1000)
);
if (!productLimiter.tryAcquire()) {
log.warn("秒杀商品限流触发: productId={}", productId);
return false;
}
// 3. 检查用户维度限流
TokenBucketRateLimiter userLimiter = userLimiters.computeIfAbsent(
userId,
k -> new TokenBucketRateLimiter(10, 1) // 每用户每秒1次
);
if (!userLimiter.tryAcquire()) {
log.warn("秒杀用户限流触发: userId={}", userId);
return false;
}
return true;
}
/**
* 预热限流器
*/
public void warmUp(String productId, long expectedQPS) {
TokenBucketRateLimiter limiter = productLimiters.computeIfAbsent(
productId,
k -> new TokenBucketRateLimiter(expectedQPS * 2, expectedQPS)
);
log.info("预热秒杀限流器: productId={}, expectedQPS={}", productId, expectedQPS);
}
}
效果:
- 全局限流保护系统不被冲垮
- 商品维度限流保证热门商品公平访问
- 用户维度限流防止刷单
- 支持突发流量,提升用户体验
案例2:微博热点事件限流
场景描述:
- 明星公布恋情等热点事件
- 流量从50万QPS激增到500万QPS
- 系统容量200万QPS
限流策略:
/**
* 微博热点事件限流器
*/
@Component
public class WeiboHotTopicRateLimiter {
private static final Logger log = LoggerFactory.getLogger(WeiboHotTopicRateLimiter.class);
private final TokenBucketRateLimiter globalLimiter;
private final ConcurrentHashMap<String, TokenBucketRateLimiter> topicLimiters;
private final ConcurrentHashMap<String, TokenBucketRateLimiter> userLimiters;
private final ConcurrentHashMap<String, TokenBucketRateLimiter> regionLimiters;
public WeiboHotTopicRateLimiter() {
// 全局限流:200万QPS,桶容量300万(支持突发)
this.globalLimiter = new TokenBucketRateLimiter(3000000, 2000000);
this.topicLimiters = new ConcurrentHashMap<>();
this.userLimiters = new ConcurrentHashMap<>();
this.regionLimiters = new ConcurrentHashMap<>();
log.info("微博热点事件限流器初始化完成");
}
/**
* 检查请求是否允许
*/
public boolean allowRequest(String topicId, String userId, String region) {
// 1. 检查全局限流
if (!globalLimiter.tryAcquire()) {
log.warn("全局限流触发");
return false;
}
// 2. 检查话题维度限流
if (topicId != null) {
TokenBucketRateLimiter topicLimiter = topicLimiters.computeIfAbsent(
topicId,
k -> new TokenBucketRateLimiter(500000, 100000)
);
if (!topicLimiter.tryAcquire()) {
log.warn("话题限流触发: topicId={}", topicId);
return false;
}
}
// 3. 检查用户维度限流
if (userId != null) {
TokenBucketRateLimiter userLimiter = userLimiters.computeIfAbsent(
userId,
k -> new TokenBucketRateLimiter(100, 10)
);
if (!userLimiter.tryAcquire()) {
log.warn("用户限流触发: userId={}", userId);
return false;
}
}
// 4. 检查地域维度限流
if (region != null) {
TokenBucketRateLimiter regionLimiter = regionLimiters.computeIfAbsent(
region,
k -> new TokenBucketRateLimiter(200000, 50000)
);
if (!regionLimiter.tryAcquire()) {
log.warn("地域限流触发: region={}", region);
return false;
}
}
return true;
}
/**
* 动态调整限流阈值
*/
public void adjustThreshold(String dimension, String key, long newRate) {
switch (dimension) {
case "global":
log.info("调整全局限流阈值: newRate={}", newRate);
// 重新创建全局限流器
break;
case "topic":
TokenBucketRateLimiter topicLimiter = topicLimiters.get(key);
if (topicLimiter != null) {
log.info("调整话题限流阈值: topicId={}, newRate={}", key, newRate);
}
break;
case "user":
TokenBucketRateLimiter userLimiter = userLimiters.get(key);
if (userLimiter != null) {
log.info("调整用户限流阈值: userId={}, newRate={}", key, newRate);
}
break;
case "region":
TokenBucketRateLimiter regionLimiter = regionLimiters.get(key);
if (regionLimiter != null) {
log.info("调整地域限流阈值: region={}, newRate={}", key, newRate);
}
break;
}
}
}
限流策略分析:
| 维度 | 平时阈值 | 突发阈值 | 说明 |
|---|---|---|---|
| 全局 | 50万QPS | 200万QPS | 保护系统不被冲垮 |
| 话题 | 1万QPS | 10万QPS | 热门话题限流 |
| 用户 | 10 QPS | 10 QPS | 防止刷屏 |
| 地域 | 5万QPS | 5万QPS | 地域流量控制 |
案例3:API网关综合限流
场景描述:
- 多个API接口
- 不同接口不同限流策略
- 需要保护后端服务
限流策略:
/**
* API网关综合限流器
*/
@Component
public class ApiGatewayComprehensiveRateLimiter {
private static final Logger log = LoggerFactory.getLogger(ApiGatewayComprehensiveRateLimiter.class);
private final TokenBucketRateLimiter globalLimiter;
private final Map<String, ApiRateLimitConfig> apiConfigs;
private final ConcurrentHashMap<String, TokenBucketRateLimiter> apiLimiters;
private final ConcurrentHashMap<String, TokenBucketRateLimiter> userLimiters;
private final ConcurrentHashMap<String, TokenBucketRateLimiter> ipLimiters;
/**
* API限流配置
*/
@Data
@Builder
public static class ApiRateLimitConfig {
private String apiPath;
private long capacity;
private long rate;
private boolean enableUserLimit;
private boolean enableIpLimit;
}
public ApiGatewayComprehensiveRateLimiter() {
// 全局限流:10000 QPS,桶容量20000
this.globalLimiter = new TokenBucketRateLimiter(20000, 10000);
this.apiConfigs = new ConcurrentHashMap<>();
this.apiLimiters = new ConcurrentHashMap<>();
this.userLimiters = new ConcurrentHashMap<>();
this.ipLimiters = new ConcurrentHashMap<>();
// 加载API限流配置
loadApiConfigs();
log.info("API网关综合限流器初始化完成");
}
/**
* 加载API限流配置
*/
private void loadApiConfigs() {
// 用户登录API
apiConfigs.put("/api/user/login", ApiRateLimitConfig.builder()
.apiPath("/api/user/login")
.capacity(2000)
.rate(1000)
.enableUserLimit(true)
.enableIpLimit(true)
.build());
// 订单创建API
apiConfigs.put("/api/order/create", ApiRateLimitConfig.builder()
.apiPath("/api/order/create")
.capacity(1000)
.rate(500)
.enableUserLimit(true)
.enableIpLimit(false)
.build());
// 商品列表API
apiConfigs.put("/api/product/list", ApiRateLimitConfig.builder()
.apiPath("/api/product/list")
.capacity(4000)
.rate(2000)
.enableUserLimit(false)
.enableIpLimit(true)
.build());
}
/**
* 检查请求是否允许
*/
public RateLimitResult checkRequest(RequestContext context) {
String apiPath = context.getApiPath();
String userId = context.getUserId();
String clientIp = context.getClientIp();
// 1. 检查全局限流
if (!globalLimiter.tryAcquire()) {
log.warn("全局限流触发: api={}", apiPath);
return RateLimitResult.rejected("全局限流", "global");
}
// 2. 检查API级别限流
ApiRateLimitConfig config = apiConfigs.get(apiPath);
if (config != null) {
TokenBucketRateLimiter apiLimiter = apiLimiters.computeIfAbsent(
apiPath,
k -> new TokenBucketRateLimiter(config.getCapacity(), config.getRate())
);
if (!apiLimiter.tryAcquire()) {
log.warn("API限流触发: api={}", apiPath);
return RateLimitResult.rejected("API限流", "api");
}
// 3. 检查用户级别限流
if (config.isEnableUserLimit() && userId != null) {
TokenBucketRateLimiter userLimiter = userLimiters.computeIfAbsent(
userId,
k -> new TokenBucketRateLimiter(100, 10)
);
if (!userLimiter.tryAcquire()) {
log.warn("用户限流触发: userId={}", userId);
return RateLimitResult.rejected("用户限流", "user");
}
}
// 4. 检查IP级别限流
if (config.isEnableIpLimit() && clientIp != null) {
TokenBucketRateLimiter ipLimiter = ipLimiters.computeIfAbsent(
clientIp,
k -> new TokenBucketRateLimiter(50, 5)
);
if (!ipLimiter.tryAcquire()) {
log.warn("IP限流触发: ip={}", clientIp);
return RateLimitResult.rejected("IP限流", "ip");
}
}
}
return RateLimitResult.allowed();
}
/**
* 限流结果
*/
@Data
@AllArgsConstructor
public static class RateLimitResult {
private boolean allowed;
private String reason;
private String dimension;
public static RateLimitResult allowed() {
return new RateLimitResult(true, null, null);
}
public static RateLimitResult rejected(String reason, String dimension) {
return new RateLimitResult(false, reason, dimension);
}
}
}
限流工具与框架
开源限流框架
1. Guava RateLimiter
Google Guava提供的令牌桶限流器实现。
import com.google.common.util.concurrent.RateLimiter;
/**
* Guava RateLimiter示例
*/
public class GuavaRateLimiterExample {
private final RateLimiter rateLimiter;
public GuavaRateLimiterExample(double permitsPerSecond) {
// 创建令牌桶限流器,每秒生成permitsPerSecond个令牌
this.rateLimiter = RateLimiter.create(permitsPerSecond);
}
/**
* 尝试获取许可
*/
public boolean tryAcquire() {
return rateLimiter.tryAcquire();
}
/**
* 尝试获取许可(带超时)
*/
public boolean tryAcquire(int timeout, TimeUnit unit) {
return rateLimiter.tryAcquire(timeout, unit);
}
/**
* 阻塞获取许可
*/
public void acquire() {
rateLimiter.acquire();
}
/**
* 获取多个许可
*/
public void acquire(int permits) {
rateLimiter.acquire(permits);
}
}
2. Sentinel
阿里巴巴开源的流量控制框架。
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
/**
* Sentinel限流示例
*/
public class SentinelRateLimiterExample {
/**
* 初始化限流规则
*/
public void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
// 创建限流规则
FlowRule rule = new FlowRule();
rule.setResource("hello");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(20); // 限流阈值:20 QPS
rules.add(rule);
// 加载规则
FlowRuleManager.loadRules(rules);
}
/**
* 使用限流
*/
public void doSomething() {
try (Entry entry = SphU.entry("hello")) {
// 被保护的逻辑
System.out.println("Hello world");
} catch (BlockException ex) {
// 被限流
System.out.println("Blocked!");
}
}
}
3. Resilience4j
轻量级的容错库,支持限流、熔断等功能。
import io.github.resilience4j.ratelimiter.RateLimiter;
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
import java.time.Duration;
/**
* Resilience4j限流示例
*/
public class Resilience4jRateLimiterExample {
private final RateLimiter rateLimiter;
public Resilience4jRateLimiterExample() {
// 配置限流器
RateLimiterConfig config = RateLimiterConfig.custom()
.limitForPeriod(10) // 周期内允许的请求数
.limitRefreshPeriod(Duration.ofSeconds(1)) // 刷新周期
.timeoutDuration(Duration.ofMillis(100)) // 等待超时
.build();
// 创建限流器
this.rateLimiter = RateLimiter.of("myLimiter", config);
}
/**
* 使用限流器
*/
public void doSomething() {
// 获取许可
if (rateLimiter.acquirePermission()) {
// 执行业务逻辑
System.out.println("Request allowed");
} else {
// 被限流
System.out.println("Request blocked");
}
}
}
分布式限流方案
基于Redis的分布式限流
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.util.Arrays;
/**
* Redis分布式限流器
*/
@Component
public class RedisDistributedRateLimiter {
private static final Logger log = LoggerFactory.getLogger(RedisDistributedRateLimiter.class);
private final StringRedisTemplate redisTemplate;
// 限流Lua脚本
private static final String RATE_LIMIT_SCRIPT =
"local key = KEYS[1]\n" +
"local limit = tonumber(ARGV[1])\n" +
"local expire = tonumber(ARGV[2])\n" +
"local current = tonumber(redis.call('GET', key) or 0)\n" +
"\n" +
"if current + 1 > limit then\n" +
" return 0\n" +
"else\n" +
" redis.call('INCR', key)\n" +
" redis.call('EXPIRE', key, expire)\n" +
" return 1\n" +
"end";
public RedisDistributedRateLimiter(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 尝试获取许可
* @param key 限流键
* @param limit 限流阈值
* @param expire 过期时间(秒)
* @return true表示允许,false表示被拒绝
*/
public boolean tryAcquire(String key, long limit, long expire) {
DefaultRedisScript<Long> script = new DefaultRedisScript<>(RATE_LIMIT_SCRIPT, Long.class);
Long result = redisTemplate.execute(
script,
Arrays.asList(key),
String.valueOf(limit),
String.valueOf(expire)
);
boolean allowed = result != null && result == 1;
if (!allowed) {
log.warn("Redis限流: key={}, limit={}", key, limit);
}
return allowed;
}
}
总结
限流法则是现代分布式系统架构中不可或缺的核心法则之一。通过合理运用计数器、漏桶、令牌桶等限流算法,我们能够有效控制系统流量,保护系统稳定性,提升用户体验。
核心原则
- 流量控制优先:在设计系统时就要考虑限流策略,而不是事后补救
- 分层防护:在不同层级实施限流,形成多道防护屏障
- 多维度限流:从全局、API、用户、IP等多个维度进行限流
- 动态调整:根据系统负载和业务情况动态调整限流阈值
- 优雅降级:限流时提供降级服务,保证核心功能可用
算法选择指南
| 场景 | 推荐算法 | 原因 |
|---|---|---|
| 简单限流 | 固定窗口计数器 | 实现简单,资源消耗低 |
| 精确限流 | 滑动窗口计数器 | 精确度高,无临界突变 |
| 保护下游 | 漏桶算法 | 流量平滑,输出恒定 |
| 突发流量 | 令牌桶算法 | 支持突发,灵活配置 |
| 分布式场景 | Redis令牌桶 | 分布式支持,原子性保证 |
实施建议
- 容量规划:提前进行容量规划,确定合理的限流阈值
- 监控告警:建立完善的监控告警机制,及时发现异常
- 压力测试:定期进行压力测试,验证限流策略的有效性
- 文档规范:建立限流配置文档,便于团队协作
- 持续优化:根据业务发展和系统变化持续优化限流策略
关键技术点
- 原子性保证:使用CAS、分布式锁、Lua脚本等保证限流操作的原子性
- 性能优化:选择合适的数据结构和算法,降低限流对性能的影响
- 内存管理:合理设计限流器的内存占用,避免内存泄漏
- 线程安全:确保限流器在多线程环境下的正确性
- 异常处理:完善异常处理机制,避免限流器本身成为瓶颈
限流不是目的,而是手段。通过合理的限流策略,我们能够在保障系统稳定性的同时,为用户提供更好的服务体验。在实际应用中,需要根据具体的业务场景和系统特点,选择合适的限流算法和策略,并持续优化调整,以达到最佳的效果。
85万+

被折叠的 条评论
为什么被折叠?



