限流算法详解
本章导读
限流是保护系统稳定性的第一道防线。本章将深入剖析固定窗口、滑动窗口、令牌桶、漏桶四种主流限流算法的原理与实现,并结合分布式场景讲解 Redis + Lua 的生产级方案。
学习目标:
- 目标1:理解四种限流算法的工作原理,能够分析它们的优缺点和适用场景
- 目标2:掌握基于 Redis + Lua 的分布式限流实现,理解原子性保证的重要性
- 目标3:能够设计多级限流方案,实现本地限流与分布式限流的组合策略
前置知识:已完成 Redis 相关章节的学习,理解分布式系统基本概念
阅读时长:约 35 分钟
一、知识概述
限流是保护系统的重要手段,通过控制请求速率,防止系统因过载而崩溃。本文将深入讲解主流的限流算法及其实现:
- 固定窗口算法
- 滑动窗口算法
- 令牌桶算法
- 漏桶算法
- 分布式限流
- 限流框架实践
二、为什么需要限流
2.1 系统面临的问题
正常流量 ──────→ [服务] ──────→ 正常响应
↓
突发流量 ──────→ [服务] ──────→ 系统崩溃/响应超时
常见场景:
- 秒杀活动瞬间流量激增
- 恶意攻击或爬虫
- 下游服务响应慢导致的请求堆积
- 上游服务异常重试风暴
2.2 限流的目标
- 保护系统:防止系统因过载而崩溃
- 公平分配资源:确保关键业务优先
- 降级保护:在系统压力过大时优雅降级
- 成本控制:控制资源消耗
三、固定窗口算法
3.1 原理
将时间划分为固定大小的窗口,在每个窗口内统计请求数量,超过阈值则拒绝。
时间线(窗口大小=1秒,阈值=5):
|--窗口1--|--窗口2--|--窗口3--|
3次 5次 2次
允许 允许
3.2 实现
/**
* 固定窗口限流器
*/
public class FixedWindowRateLimiter {
private final long windowSize; // 窗口大小(毫秒)
private final int limit; // 窗口内最大请求数
private final AtomicInteger counter; // 当前窗口计数
private final AtomicLong windowStart; // 当前窗口开始时间
public FixedWindowRateLimiter(long windowSizeMs, int limit) {
this.windowSize = windowSizeMs;
this.limit = limit;
this.counter = new AtomicInteger(0);
this.windowStart = new AtomicLong(System.currentTimeMillis());
}
/**
* 尝试获取许可
*/
public synchronized boolean tryAcquire() {
long now = System.currentTimeMillis();
long currentWindowStart = windowStart.get();
// 检查是否需要切换窗口
if (now - currentWindowStart >= windowSize) {
// 新窗口,重置计数
windowStart.set(now);
counter.set(0);
}
// 检查是否超过限制
if (counter.get() >= limit) {
return false;
}
counter.incrementAndGet();
return true;
}
/**
* 获取当前窗口状态
*/
public WindowStatus getStatus() {
long now = System.currentTimeMillis();
long currentWindowStart = windowStart.get();
long remainingTime = windowSize - (now - currentWindowStart);
return new WindowStatus(
counter.get(),
limit,
Math.max(0, remainingTime)
);
}
@Data
@AllArgsConstructor
public static class WindowStatus {
private int currentCount;
private int limit;
private long remainingTimeMs;
}
}
3.3 问题:边界突发
窗口大小=1秒,阈值=5
|--窗口1--|--窗口2--|
↑ ↑
0.9秒 1.1秒
5次请求 5次请求
在2秒内实际处理了10次请求,在窗口边界存在突发流量问题。
四、滑动窗口算法
4.1 原理
将窗口划分为多个小格子,滑动统计最近N个格子的请求数。
窗口大小=1秒,格子数=10,每个格子=100ms
格子: [0][1][2][3][4][5][6][7][8][9]
←─────── 滑动窗口 ───────→
时间流逝,窗口向前滑动,丢弃最旧的格子,添加新格子。
4.2 实现
/**
* 滑动窗口限流器
*/
public class SlidingWindowRateLimiter {
private final long windowSize; // 窗口大小(毫秒)
private final int limit; // 窗口内最大请求数
private final int subWindowCount; // 子窗口数量
private final long subWindowSize; // 子窗口大小
private final int[] counters; // 每个子窗口的计数
private final AtomicLong currentWindowStart; // 当前子窗口开始时间
public SlidingWindowRateLimiter(long windowSizeMs, int limit, int subWindowCount) {
this.windowSize = windowSizeMs;
this.limit = limit;
this.subWindowCount = subWindowCount;
this.subWindowSize = windowSizeMs / subWindowCount;
this.counters = new int[subWindowCount];
this.currentWindowStart = new AtomicLong(System.currentTimeMillis());
}
public SlidingWindowRateLimiter(long windowSizeMs, int limit) {
this(windowSizeMs, limit, 10); // 默认10个子窗口
}
/**
* 尝试获取许可
*/
public synchronized boolean tryAcquire() {
long now = System.currentTimeMillis();
// 计算当前子窗口索引
int currentIndex = (int) ((now / subWindowSize) % subWindowCount);
long currentWindowStartTime = now - now % subWindowSize;
// 检查是否需要重置子窗口
if (currentWindowStart.get() != currentWindowStartTime) {
// 清理过期的子窗口
cleanExpiredWindows(now);
currentWindowStart.set(currentWindowStartTime);
}
// 计算当前窗口内的总请求数
int totalCount = calculateTotalCount(currentIndex);
// 检查是否超过限制
if (totalCount >= limit) {
return false;
}
counters[currentIndex]++;
return true;
}
/**
* 清理过期的子窗口
*/
private void cleanExpiredWindows(long now) {
for (int i = 0; i < subWindowCount; i++) {
// 计算该子窗口的时间范围
long windowStartTime = now - now % subWindowSize -
(subWindowCount - i) * subWindowSize;
long windowEndTime = windowStartTime + subWindowSize;
// 如果子窗口已经过期,重置计数
if (windowEndTime <= now - windowSize) {
counters[i] = 0;
}
}
}
/**
* 计算当前窗口内的总请求数
*/
private int calculateTotalCount(int currentIndex) {
int total = 0;
for (int i = 0; i < subWindowCount; i++) {
total += counters[i];
}
return total;
}
}
4.3 基于Redis的分布式滑动窗口
/**
* Redis滑动窗口限流器
*/
@Service
public class RedisSlidingWindowLimiter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String SCRIPT = """
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
-- 删除过期记录
redis.call('zremrangebyscore', key, 0, now - window * 1000)
-- 统计当前窗口请求数
local count = redis.call('zcard', key)
if count < limit then
-- 添加当前请求记录
redis.call('zadd', key, now, now .. ':' .. math.random())
redis.call('expire', key, window)
return 1
else
return 0
end
""";
public boolean tryAcquire(String key, int limit, long windowSeconds) {
String redisKey = "rate_limit:" + key;
long now = System.currentTimeMillis();
DefaultRedisScript<Long> script = new DefaultRedisScript<>(SCRIPT, Long.class);
Long result = redisTemplate.execute(
script,
Collections.singletonList(redisKey),
String.valueOf(limit),
String.valueOf(windowSeconds),
String.valueOf(now)
);
return result != null && result == 1L;
}
/**
* 获取当前窗口请求数
*/
public long getCurrentCount(String key, long windowSeconds) {
String redisKey = "rate_limit:" + key;
long now = System.currentTimeMillis();
redisTemplate.opsForZSet().removeRangeByScore(redisKey, 0, now - windowSeconds * 1000);
Long count = redisTemplate.opsForZSet().size(redisKey);
return count != null ? count : 0;
}
}
五、令牌桶算法
5.1 原理
以固定速率生成令牌放入桶中,请求到达时从桶中取令牌,有令牌则通过,无令牌则拒绝。
┌─────────────┐
令牌生成 ──→│ 令牌桶 │
(r/秒) │ 容量=b │──→ 请求取令牌 ──→ 通过/拒绝
└─────────────┘
- 桶容量:最多存放b个令牌
- 生成速率:每秒生成r个令牌
- 桶满后新令牌丢弃
5.2 特点
- 允许突发:桶中有令牌时可以快速处理一批请求
- 平滑限流:长期来看,速率被限制在r/秒
- 灵活:可以配置不同的令牌消耗量
5.3 实现
/**
* 令牌桶限流器
*/
public class TokenBucketRateLimiter {
private final long capacity; // 桶容量
private final double rate; // 令牌生成速率(个/毫秒)
private final AtomicLong tokens; // 当前令牌数
private final AtomicLong lastRefillTime; // 上次填充时间
public TokenBucketRateLimiter(long capacity, double ratePerSecond) {
this.capacity = capacity;
this.rate = ratePerSecond / 1000; // 转换为每毫秒
this.tokens = new AtomicLong(capacity);
this.lastRefillTime = new AtomicLong(System.currentTimeMillis());
}
/**
* 尝试获取令牌
*/
public boolean tryAcquire() {
return tryAcquire(1);
}
/**
* 尝试获取指定数量令牌
*/
public synchronized boolean tryAcquire(long tokensRequested) {
refill();
long currentTokens = tokens.get();
if (currentTokens >= tokensRequested) {
tokens.addAndGet(-tokensRequested);
return true;
}
return false;
}
/**
* 填充令牌
*/
private void refill() {
long now = System.currentTimeMillis();
long lastRefill = lastRefillTime.get();
if (now > lastRefill) {
// 计算应该生成的令牌数
long elapsed = now - lastRefill;
long tokensToAdd = (long) (elapsed * rate);
if (tokensToAdd > 0) {
// 更新令牌数(不超过容量)
long newTokens = Math.min(capacity, tokens.get() + tokensToAdd);
tokens.set(newTokens);
lastRefillTime.set(now);
}
}
}
/**
* 获取当前状态
*/
public BucketStatus getStatus() {
refill();
return new BucketStatus(tokens.get(), capacity, rate * 1000);
}
@Data
@AllArgsConstructor
public static class BucketStatus {
private long currentTokens;
private long capacity;
private double ratePerSecond;
}
}
5.4 Guava RateLimiter
import com.google.common.util.concurrent.RateLimiter;
@Service
public class GuavaRateLimiterService {
// 创建令牌桶限流器,每秒生成10个令牌
private final RateLimiter rateLimiter = RateLimiter.create(10.0);
/**
* 尝试获取许可(非阻塞)
*/
public boolean tryAcquire() {
return rateLimiter.tryAcquire();
}
/**
* 尝试获取许可(带超时)
*/
public boolean tryAcquire(long timeout, TimeUnit unit) {
return rateLimiter.tryAcquire(timeout, unit);
}
/**
* 获取许可(阻塞等待)
*/
public void acquire() {
rateLimiter.acquire();
}
/**
* 获取指定数量许可
*/
public void acquire(int permits) {
rateLimiter.acquire(permits);
}
/**
* 预热版令牌桶(适用于需要预热期的场景)
*/
public RateLimiter createWarmup(double permitsPerSecond, long warmupPeriod, TimeUnit unit) {
// 在预热期内,令牌生成速率逐渐增加
return RateLimiter.create(permitsPerSecond, warmupPeriod, unit);
}
}
六、漏桶算法
6.1 原理
请求像水一样流入桶中,桶以固定速率漏水(处理请求),桶满则溢出(拒绝请求)。
┌─────────────┐
请求流入 ──────→│ 漏桶 │
│ 容量=b │──→ 固定速率流出 ──→ 处理
│ 漏出=r/秒 │
└─────────────┘
↓
桶满溢出 ──→ 拒绝
6.2 特点
- 强制恒定速率:流出速率固定,不能突发
- 平滑流量:将不规则流量整形为恒定流量
- 保护下游:防止突发流量冲击下游系统
6.3 实现
/**
* 漏桶限流器
*/
public class LeakyBucketRateLimiter {
private final long capacity; // 桶容量
private final double rate; // 漏水速率(个/毫秒)
private final AtomicLong water; // 当前水量
private final AtomicLong lastLeakTime; // 上次漏水时间
public LeakyBucketRateLimiter(long capacity, double ratePerSecond) {
this.capacity = capacity;
this.rate = ratePerSecond / 1000;
this.water = new AtomicLong(0);
this.lastLeakTime = new AtomicLong(System.currentTimeMillis());
}
/**
* 尝试添加水滴(请求)
*/
public synchronized boolean tryAcquire() {
leak();
if (water.get() < capacity) {
water.incrementAndGet();
return true;
}
return false;
}
/**
* 漏水
*/
private void leak() {
long now = System.currentTimeMillis();
long lastLeak = lastLeakTime.get();
if (now > lastLeak) {
// 计算应该漏出的水量
long elapsed = now - lastLeak;
long leaked = (long) (elapsed * rate);
if (leaked > 0) {
// 更新水量(不低于0)
long newWater = Math.max(0, water.get() - leaked);
water.set(newWater);
lastLeakTime.set(now);
}
}
}
/**
* 获取当前状态
*/
public BucketStatus getStatus() {
leak();
return new BucketStatus(water.get(), capacity, rate * 1000);
}
@Data
@AllArgsConstructor
public static class BucketStatus {
private long currentWater;
private long capacity;
private double ratePerSecond;
}
}
七、算法对比
7.1 对比表格
| 算法 | 允许突发 | 流量整形 | 实现复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|---|---|
| 固定窗口 | 是(边界) | 差 | 低 | O(1) | 简单限流 |
| 滑动窗口 | 否 | 好 | 中 | O(n) | 精确限流 |
| 令牌桶 | 是 | 中 | 中 | O(1) | API限流 |
| 漏桶 | 否 | 好 | 中 | O(1) | 流量整形 |
7.2 选择建议
┌─────────────────────┐
│ 是否需要允许突发流量?│
└──────────┬──────────┘
│
┌───────────────┴───────────────┐
│ 是 │ 否
↓ ↓
┌─────────────────┐ ┌─────────────────┐
│ 令牌桶算法 │ │ 是否需要精确限流?│
└─────────────────┘ └────────┬────────┘
│
┌───────────────┴───────────────┐
│ 是 │ 否
↓ ↓
┌─────────────────┐ ┌─────────────────┐
│ 滑动窗口算法 │ │ 漏桶算法 │
└─────────────────┘ └─────────────────┘
八、分布式限流
8.1 Redis + Lua实现
/**
* 分布式令牌桶限流器
*/
@Service
public class DistributedTokenBucketLimiter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String SCRIPT = """
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 lastTime = tonumber(info[2]) or now
-- 计算新生成的令牌
local delta = math.max(0, now - lastTime)
local newTokens = delta / 1000 * rate
-- 更新令牌数
tokens = math.min(capacity, tokens + newTokens)
-- 判断是否足够
if tokens >= requested then
tokens = tokens - requested
redis.call('hmset', key, 'tokens', tokens, 'last_time', now)
redis.call('expire', key, math.ceil(capacity / rate) + 1)
return {1, tokens}
else
redis.call('hmset', key, 'tokens', tokens, 'last_time', now)
redis.call('expire', key, math.ceil(capacity / rate) + 1)
return {0, tokens}
end
""";
/**
* 尝试获取令牌
*/
public boolean tryAcquire(String key, long capacity, double ratePerSecond, long requested) {
String redisKey = "token_bucket:" + key;
long now = System.currentTimeMillis();
DefaultRedisScript<List> script = new DefaultRedisScript<>(SCRIPT, List.class);
List<Long> result = redisTemplate.execute(
script,
Collections.singletonList(redisKey),
String.valueOf(capacity),
String.valueOf(ratePerSecond),
String.valueOf(requested),
String.valueOf(now)
);
return result != null && !result.isEmpty() && result.get(0) == 1L;
}
}
8.2 集群限流(Redis Cluster)
/**
* 集群限流服务
*/
@Service
public class ClusterRateLimiter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 集群总限流 + 本地限流
* 解决Redis集群限流的性能问题
*/
public boolean tryAcquire(String key, int globalLimit, int localLimit) {
// 第一层:本地限流(快速失败)
if (!localTryAcquire(key, localLimit)) {
return false;
}
// 第二层:全局限流(精确控制)
return globalTryAcquire(key, globalLimit);
}
// 本地限流器缓存
private final Cache<String, TokenBucketRateLimiter> localLimiters =
Caffeine.newBuilder()
.expireAfterAccess(1, TimeUnit.HOURS)
.build();
private boolean localTryAcquire(String key, int limit) {
TokenBucketRateLimiter limiter = localLimiters.get(key,
k -> new TokenBucketRateLimiter(limit, limit));
return limiter.tryAcquire();
}
private boolean globalTryAcquire(String key, int limit) {
// 使用Redis滑动窗口
return redisTryAcquire(key, limit, 1);
}
private boolean redisTryAcquire(String key, int limit, int windowSeconds) {
// ... Redis滑动窗口实现
return true;
}
}
九、限流框架实践
9.1 Spring Boot + 自定义注解
// 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
String key() default "";
int limit() default 100;
int window() default 1; // 秒
LimitType type() default LimitType.TOKEN_BUCKET;
String message() default "请求过于频繁,请稍后再试";
}
public enum LimitType {
FIXED_WINDOW,
SLIDING_WINDOW,
TOKEN_BUCKET,
LEAKY_BUCKET
}
// 切面实现
@Aspect
@Component
@Slf4j
public class RateLimitAspect {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
@Autowired
private RedisSlidingWindowLimiter redisLimiter;
@Around("@annotation(rateLimit)")
public Object around(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
String key = generateKey(point, rateLimit);
boolean allowed = false;
switch (rateLimit.type()) {
case TOKEN_BUCKET:
allowed = getTokenBucketLimiter(key, rateLimit).tryAcquire();
break;
case SLIDING_WINDOW:
allowed = redisLimiter.tryAcquire(key, rateLimit.limit(), rateLimit.window());
break;
case FIXED_WINDOW:
allowed = fixedWindowTryAcquire(key, rateLimit.limit(), rateLimit.window());
break;
default:
allowed = true;
}
if (!allowed) {
throw new RateLimitException(rateLimit.message());
}
return point.proceed();
}
private RateLimiter getTokenBucketLimiter(String key, RateLimit rateLimit) {
return limiters.computeIfAbsent(key,
k -> RateLimiter.create(rateLimit.limit()));
}
private String generateKey(ProceedingJoinPoint point, RateLimit rateLimit) {
if (!rateLimit.key().isEmpty()) {
return rateLimit.key();
}
MethodSignature signature = (MethodSignature) point.getSignature();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
return className + ":" + methodName;
}
}
// 异常处理
@RestControllerAdvice
public class RateLimitExceptionHandler {
@ExceptionHandler(RateLimitException.class)
public ResponseEntity<ErrorResponse> handleRateLimitException(RateLimitException e) {
return ResponseEntity
.status(HttpStatus.TOO_MANY_REQUESTS)
.body(new ErrorResponse(429, e.getMessage()));
}
}
@Data
@AllArgsConstructor
class ErrorResponse {
private int code;
private String message;
}
class RateLimitException extends RuntimeException {
public RateLimitException(String message) {
super(message);
}
}
9.2 使用示例
@RestController
@RequestMapping("/api")
public class ApiController {
/**
* 令牌桶限流:每秒最多100次
*/
@GetMapping("/data")
@RateLimit(limit = 100, message = "API调用过于频繁")
public String getData() {
return "data";
}
/**
* 滑动窗口限流:每秒最多10次
*/
@PostMapping("/submit")
@RateLimit(limit = 10, type = LimitType.SLIDING_WINDOW, message = "提交太频繁了")
public String submit(@RequestBody String data) {
return "submitted";
}
/**
* IP级别限流
*/
@GetMapping("/search")
@RateLimit(key = "'search:' + #ip", limit = 50, type = LimitType.SLIDING_WINDOW)
public String search(@RequestHeader("X-Real-IP") String ip,
@RequestParam String keyword) {
return "search result";
}
}
9.3 Sentinel限流
/**
* Sentinel限流集成
*/
@Configuration
public class SentinelConfig {
@PostConstruct
public void init() {
// 初始化规则
List<FlowRule> rules = new ArrayList<>();
// API限流规则
FlowRule apiRule = new FlowRule();
apiRule.setResource("api:getData");
apiRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
apiRule.setCount(100); // QPS限制100
rules.add(apiRule);
FlowRuleManager.loadRules(rules);
}
}
@RestController
public class SentinelController {
@GetMapping("/api/data")
public String getData() {
try (Entry entry = SphU.entry("api:getData")) {
// 业务逻辑
return "data";
} catch (BlockException e) {
// 被限流
throw new RateLimitException("系统繁忙,请稍后再试");
}
}
/**
* 使用注解方式
*/
@SentinelResource(value = "api:submit", blockHandler = "handleBlock")
@PostMapping("/api/submit")
public String submit(@RequestBody String data) {
return "submitted";
}
public String handleBlock(String data, BlockException e) {
return "系统繁忙,请稍后再试";
}
}
十、高级应用
10.1 自适应限流
/**
* 自适应限流器
* 根据系统负载动态调整限流阈值
*/
@Service
public class AdaptiveRateLimiter {
private final AtomicReference<Double> currentLimit;
private final double minLimit;
private final double maxLimit;
public AdaptiveRateLimiter(double minLimit, double maxLimit) {
this.minLimit = minLimit;
this.maxLimit = maxLimit;
this.currentLimit = new AtomicReference<>(maxLimit);
}
/**
* 定期更新限流阈值
*/
@Scheduled(fixedRate = 1000)
public void updateLimit() {
// 获取系统指标
double cpuUsage = getCpuUsage();
double memoryUsage = getMemoryUsage();
double avgResponseTime = getAvgResponseTime();
// 计算新的限流阈值
double factor = calculateFactor(cpuUsage, memoryUsage, avgResponseTime);
double newLimit = maxLimit * factor;
newLimit = Math.max(minLimit, Math.min(maxLimit, newLimit));
currentLimit.set(newLimit);
}
private double calculateFactor(double cpuUsage, double memoryUsage, double avgResponseTime) {
// CPU权重
double cpuFactor = 1.0 - (cpuUsage / 100.0) * 0.5;
// 内存权重
double memoryFactor = 1.0 - (memoryUsage / 100.0) * 0.3;
// 响应时间权重(响应时间>500ms认为压力大)
double rtFactor = avgResponseTime < 500 ? 1.0 : 0.5;
return cpuFactor * memoryFactor * rtFactor;
}
public double getCurrentLimit() {
return currentLimit.get();
}
// ... 省略获取系统指标的方法
}
10.2 多级限流
/**
* 多级限流策略
*/
@Service
public class MultiLevelRateLimiter {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// L1: 本地限流器(快速失败)
private final Cache<String, RateLimiter> localLimiters =
Caffeine.newBuilder().build();
/**
* 三级限流:本地 -> 分布式 -> 降级
*/
public <T> T execute(String resource, MultiLevelLimitConfig config,
Supplier<T> supplier, Supplier<T> fallback) {
// L1: 本地限流(快速判断)
RateLimiter localLimiter = localLimiters.get(resource,
k -> RateLimiter.create(config.getLocalLimit()));
if (!localLimiter.tryAcquire()) {
return fallback.get();
}
// L2: 分布式限流(精确控制)
if (!distributedTryAcquire(resource, config.getDistributedLimit())) {
return fallback.get();
}
// L3: 执行业务逻辑
try {
return supplier.get();
} catch (Exception e) {
log.error("业务执行异常", e);
return fallback.get();
}
}
private boolean distributedTryAcquire(String resource, int limit) {
// Redis滑动窗口实现
// ...
return true;
}
}
@Data
class MultiLevelLimitConfig {
private double localLimit; // 本地限流QPS
private int distributedLimit; // 分布式限流QPS
}
十一、总结
11.1 核心要点
- 选择合适的算法:根据业务特点选择限流算法
- 分布式场景:使用Redis + Lua保证原子性
- 多级防护:本地限流 + 分布式限流
- 监控告警:实时监控限流触发情况
- 优雅降级:限流触发时返回友好提示
11.2 最佳实践
// 推荐的限流配置
@Configuration
public class RateLimitConfig {
// API接口限流
public static final int API_QPS_LIMIT = 1000;
// 用户级限流
public static final int USER_QPS_LIMIT = 10;
// IP级限流
public static final int IP_QPS_LIMIT = 100;
// 关键接口限流
public static final int CRITICAL_API_LIMIT = 50;
}
限流是系统保护的重要手段,合理配置限流策略可以有效防止系统过载,保障服务的可用性。在实际应用中,需要根据业务场景和系统特点选择合适的限流算法和阈值。 断降级详解》- 学习服务熔断与降级策略
- 扩展阅读:《亿级流量网站架构核心技术》限流章节
📝 下一章预告
下一章将学习熔断降级机制,探讨如何在系统故障时快速失败、优雅降级,防止故障扩散导致雪崩效应。
本章完