3.2 滑动窗口限流
实现思路:
- 使用 Redis 的有序集合(
ZSET
)存储请求时间戳。 - 移除窗口外(例如 1 秒前)的请求记录,统计窗口内的请求数。
- 如果请求数超过阈值,拒绝请求。
代码示例:
java
@Component
public class SlidingWindowRateLimiter implements GatewayFilterFactory<SlidingWindowRateLimiter.Config> {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String key = "sliding_window:" + exchange.getRequest().getURI().getPath();
long currentTime = System.currentTimeMillis();
long windowStart = currentTime - 1000; // 1 秒窗口
// 移除窗口外的记录
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
// 添加当前请求时间戳
redisTemplate.opsForZSet().add(key, currentTime + "", currentTime);
// 统计窗口内请求数
Long count = redisTemplate.opsForZSet().count(key, windowStart, currentTime);
if (count > config.getLimit()) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return response.setComplete();
}
return chain.filter(exchange);
};
}
public static class Config {
private int limit; // 每秒最大请求数
// getter 和 setter
}
}
配置文件:
yaml
spring:
cloud:
gateway:
routes:
- id: sliding_window_route
uri: http://localhost:8081
predicates:
- Path=/api/**
filters:
- name: SlidingWindowRateLimiter
args:
limit: 10
说明:
- 使用
ZSET
存储时间戳,动态维护 1 秒内的请求。 - 通过移除窗口外的记录,精确统计滑动窗口内的请求数。
- 相比固定窗口,滑动窗口平滑了边界流量突刺,但 Redis 操作更复杂。
3.3 令牌桶限流
实现思路:
- Spring Cloud Gateway 内置的
RequestRateLimiter
基于令牌桶算法,依赖 Redis。 - 配置
redis-rate-limiter.replenishRate
(令牌补充速率)和redis-rate-limiter.burstCapacity
(桶容量)。
代码示例: Spring Cloud Gateway 的默认令牌桶实现无需额外编码,只需配置即可:
配置文件:
yaml
spring:
redis:
host: localhost
port: 6379
cloud:
gateway:
routes:
- id: token_bucket_route
uri: http://localhost:8081
predicates:
- Path=/api/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒补充 10 个令牌
redis-rate-limiter.burstCapacity: 20 # 桶容量 20
key-resolver: "#{@pathKeyResolver}" # 按路径限流
KeyResolver 配置:
java
@Configuration
public class RateLimiterConfig {
@Bean
public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
}
说明:
replenishRate
控制令牌补充速度,burstCapacity
允许短时间内的突发流量。- 默认实现简单高效,适合大多数场景。
- 如果需要自定义令牌桶逻辑,可以参考固定窗口的 Redis 实现,维护令牌计数。
3.4 漏桶限流
实现思路:
- 使用 Redis 队列(
LIST
)模拟漏桶,请求进入队列。 - 以固定速率从队列中移除请求(模拟漏出)。
- 如果队列长度超过桶容量,拒绝新请求。
代码示例:
java
@Component
public class LeakyBucketRateLimiter implements GatewayFilterFactory<LeakyBucketRateLimiter.Config> {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String key = "leaky_bucket:" + exchange.getRequest().getURI().getPath();
// 添加请求到队列
redisTemplate.opsForList().rightPush(key, System.currentTimeMillis() + "");
Long size = redisTemplate.opsForList().size(key);
if (size > config.getCapacity()) {
// 队列超限,拒绝请求
redisTemplate.opsForList().rightPop(key); // 移除刚添加的请求
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return response.setComplete();
}
// 模拟漏桶定时漏出(实际生产中应使用定时任务)
redisTemplate.opsForList().leftPop(key); // 假设固定速率漏出
return chain.filter(exchange);
};
}
public static class Config {
private int capacity; // 桶容量
// getter 和 setter
}
}
配置文件:
yaml
spring:
cloud:
gateway:
routes:
- id: leaky_bucket_route
uri: http://localhost:8081
predicates:
- Path=/api/**
filters:
- name: LeakyBucketRateLimiter
args:
capacity: 10 # 桶容量 10
说明:
- 使用 Redis
LIST
模拟队列,请求入队,超出容量拒绝。 - 漏出速率需要额外定时任务控制(示例中简化处理)。
- 漏桶严格控制流量速率,适合需要平滑输出的场景,但实现复杂。
4. 四种方案对比
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
固定窗口 | 实现简单,性能高 | 边界突刺问题 | 对突刺不敏感的场景 |
滑动窗口 | 平滑流量,无边界突刺 | 实现复杂,Redis 开销较高 | 需要精确限流的场景 |
令牌桶 | 支持突发流量,内置支持 | 配置复杂,突发流量可能影响下游 | 通用场景,允许一定突发流量 |
漏桶 | 严格平滑流量 | 实现复杂,延迟较高 | 对流量速率要求严格的场景 |
5. 注意事项
- Redis 性能:限流依赖 Redis,需确保 Redis 性能和稳定性。
- 分布式环境:Redis 提供了分布式计数支持,但需注意网络延迟。
- 动态配置:生产环境中,建议将限流参数(如阈值、窗口大小)存储在配置中心,支持动态调整。
- 降级策略:限流触发时,应返回友好的错误信息(如 HTTP 429),并考虑降级逻辑。
6. 总结
Spring Cloud Gateway 提供了强大的扩展能力,支持实现多种限流算法:
- 固定窗口适合简单场景,易于实现。
- 滑动窗口适合需要平滑流量的场景,精度高。
- 令牌桶内置支持,适合大多数场景。
- 漏桶适合严格控制速率的场景,但实现复杂。
通过本文的代码示例,开发者可以根据实际需求选择合适的限流方案,并结合 Redis 实现高效的流量控制。希望这篇文章能为你的微服务限流设计提供帮助!