本文目录导读:

我来为您提供一个完整的Spring Cloud Gateway限流案例,涵盖多种限流策略。
项目依赖配置
Maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Redis实现令牌桶限流 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- 限流算法支持 -->
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>7.6.0</version>
</dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-redis</artifactId>
<version>7.6.0</version>
</dependency>
基础配置文件
application.yml
spring:
application:
name: gateway-service
redis:
host: localhost
port: 6379
database: 0
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 5
redis-rate-limiter.burstCapacity: 10
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@ipKeyResolver}"
限流Key解析器配置
KeyResolver配置类
@Configuration
public class RateLimiterConfig {
/**
* 基于用户ID的限流
*/
@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
// 从需要中获取用户信息
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
if (userId == null || userId.isEmpty()) {
// 默认使用IP作为标识
userId = getClientIp(exchange);
}
return Mono.just(userId);
};
}
/**
* 基于IP地址的限流
*/
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(getClientIp(exchange));
}
/**
* 基于API Key的限流
*/
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> {
String apiKey = exchange.getRequest().getHeaders().getFirst("X-API-Key");
if (apiKey == null || apiKey.isEmpty()) {
return Mono.just("unknown_api_key");
}
return Mono.just(apiKey);
};
}
private String getClientIp(ServerWebExchange exchange) {
String ip = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
if (ip == null || ip.isEmpty()) {
ip = exchange.getRequest().getRemoteAddress() != null ?
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() : "unknown";
} else {
ip = ip.split(",")[0].trim();
}
return ip;
}
}
自定义限流过滤器
自定义限流实现
@Component
public class CustomRateLimiterFilter implements GlobalFilter, Ordered {
private final ReactiveStringRedisTemplate redisTemplate;
private final RateLimiterConfig rateLimiterConfig;
@Autowired
public CustomRateLimiterFilter(ReactiveStringRedisTemplate redisTemplate,
RateLimiterConfig rateLimiterConfig) {
this.redisTemplate = redisTemplate;
this.rateLimiterConfig = rateLimiterConfig;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求路径
String path = exchange.getRequest().getURI().getPath();
// 获取客户端IP
String clientIp = exchange.getRequest().getRemoteAddress() != null ?
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() : "unknown";
// 构建Redis key
String rateLimitKey = "rate_limit:" + clientIp + ":" + path;
// 实现简单的滑动窗口限流
return isAllowed(rateLimitKey)
.flatMap(allowed -> {
if (allowed) {
return chain.filter(exchange);
} else {
// 返回429状态码
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
return exchange.getResponse().writeWith(
Mono.just(exchange.getResponse().bufferFactory()
.wrap("{\"code\":429,\"message\":\"请求过于频繁\"}".getBytes()))
);
}
});
}
private Mono<Boolean> isAllowed(String key) {
return redisTemplate.opsForValue()
.increment(key)
.flatMap(count -> {
if (count == 1) {
// 设置过期时间,例如1分钟
return redisTemplate.expire(key, Duration.ofMinutes(1))
.map(expired -> true);
}
return Mono.just(count <= 100); // 每分钟最多100次请求
});
}
@Override
public int getOrder() {
return -100; // 高优先级执行
}
}
使用Bucket4j实现更复杂限流
Bucket4j配置类
@Configuration
public class Bucket4jConfig {
private final RedisTemplate<String, Object> redisTemplate;
@Autowired
public Bucket4jConfig(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 创建基于IP的限流桶
*/
@Bean
public Bucket createIpBucket() {
// 创建Bucket4j配置
Bandwidth limit = Bandwidth.classic(10, Refill.greedy(10, Duration.ofMinutes(1)));
// 使用Redis存储桶状态
ProxyManager<String> buckets = Bucket4j.extension(Redis.class)
.proxyManagerForMap(redisTemplate.opsForValue().getOperations().getConnectionFactory());
return Bucket.builder()
.addLimit(limit)
.build();
}
/**
* 获取或创建Bucket
*/
public Bucket getOrCreateBucket(String key) {
ProxyManager<String> buckets = Bucket4j.extension(Redis.class)
.proxyManagerForMap(redisTemplate.opsForValue().getOperations().getConnectionFactory());
Function<String, Bucket> bucketConfiguration = k -> Bucket.builder()
.addLimit(Bandwidth.classic(10, Refill.greedy(10, Duration.ofMinutes(1))))
.build();
return buckets.getProxy(key, bucketConfiguration);
}
}
使用Bucket4j的过滤器
@Component
public class Bucket4jRateLimiterFilter implements GatewayFilter {
private final Bucket4jConfig bucket4jConfig;
@Autowired
public Bucket4jRateLimiterFilter(Bucket4jConfig bucket4jConfig) {
this.bucket4jConfig = bucket4jConfig;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取客户端IP作为key
String clientIp = exchange.getRequest().getRemoteAddress() != null ?
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() : "unknown";
// 获取或创建Bucket
Bucket bucket = bucket4jConfig.getOrCreateBucket(clientIp);
// 尝试获取令牌
ConsumptionProbe probe = bucket.tryConsumeAndReturnRemaining(1);
if (probe.isConsumed()) {
// 添加剩余令牌数到响应头
exchange.getResponse().getHeaders().add("X-Rate-Limit-Remaining",
String.valueOf(probe.getRemainingTokens()));
return chain.filter(exchange);
} else {
// 计算重试时间
long waitForRefill = probe.getNanosToWaitForRefill() / 1_000_000_000;
exchange.getResponse().getHeaders().add("X-Rate-Limit-Retry-After-Seconds",
String.valueOf(waitForRefill));
// 返回429状态码
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
}
}
动态限流配置
限流规则管理类
@Service
public class RateLimitRuleService {
private final RedisTemplate<String, Object> redisTemplate;
private final Map<String, RateLimitRule> ruleCache = new ConcurrentHashMap<>();
@Autowired
public RateLimitRuleService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
loadInitialRules();
}
/**
* 更新限流规则
*/
public void updateRule(String key, RateLimitRule rule) {
redisTemplate.opsForValue().set("rate_rule:" + key, rule);
ruleCache.put(key, rule);
}
/**
* 获取规则
*/
public RateLimitRule getRule(String key) {
// 先从缓存获取
RateLimitRule rule = ruleCache.get(key);
if (rule == null) {
// 从Redis获取
rule = (RateLimitRule) redisTemplate.opsForValue().get("rate_rule:" + key);
if (rule != null) {
ruleCache.put(key, rule);
}
}
return rule;
}
/**
* 动态过滤器
*/
public Mono<Void> applyDynamicRateLimit(ServerWebExchange exchange, GatewayFilterChain chain) {
String clientIp = exchange.getRequest().getRemoteAddress() != null ?
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() : "unknown";
RateLimitRule rule = getRule(clientIp);
if (rule == null) {
// 使用默认规则
rule = new RateLimitRule(10, 1, 60); // 每秒10个请求,突发1个,窗口1分钟
}
// 应用动态规则
return isAllowedWithRule(clientIp, rule)
.flatMap(allowed -> {
if (allowed) {
return chain.filter(exchange);
} else {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
}
});
}
private Mono<Boolean> isAllowedWithRule(String key, RateLimitRule rule) {
// 使用Lua脚本实现原子性操作
String script = """
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call('INCR', key)
if current == 1 then
redis.call('EXPIRE', key, window)
end
return current <= limit
""";
return redisTemplate.execute(
new DefaultRedisScript<>(script, Boolean.class),
Arrays.asList("rate:" + key),
String.valueOf(rule.getLimit()),
String.valueOf(rule.getWindowSeconds())
);
}
}
全局限流配置
全局过滤器配置
@Configuration
public class GatewayFilterConfig {
@Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> {
// 检查是否是API请求
if (exchange.getRequest().getURI().getPath().startsWith("/api/")) {
// 添加自定义头
exchange.getRequest().mutate()
.header("X-Gateway-Request-Time", String.valueOf(System.currentTimeMillis()))
.build();
}
return chain.filter(exchange);
};
}
@Bean
public GlobalFilter rateLimitGlobalFilter(RateLimitRuleService rateLimitRuleService) {
return (exchange, chain) -> rateLimitRuleService
.applyDynamicRateLimit(exchange, chain);
}
}
高级配置示例
多级限流配置
spring:
cloud:
gateway:
routes:
- id: api-routes
uri: lb://api-service
predicates:
- Path=/api/**
filters:
# 第一级:限速
- RequestRateLimiter=5, 10
# 第二级:限流
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 5
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@userKeyResolver}"
自定义错误处理
@Configuration
public class RateLimitErrorHandler {
@Bean
public ErrorWebExceptionHandler rateLimitExceptionHandler() {
return (exchange, ex) -> {
if (ex instanceof RateLimitExceededException) {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
String errorMessage = """
{
"code": 429,
"message": "请求过于频繁,请稍后再试",
"retryAfter": 60,
"timestamp": "%s"
}
""".formatted(LocalDateTime.now());
return exchange.getResponse().writeWith(
Mono.just(exchange.getResponse().bufferFactory().wrap(errorMessage.getBytes()))
);
}
return Mono.error(ex);
};
}
}
监控和指标
监控配置
@Component
public class RateLimitMetrics {
private final MeterRegistry meterRegistry;
@Autowired
public RateLimitMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
public void recordRateLimit(String key, boolean allowed) {
meterRegistry.counter("gateway.rate.limit.requests",
"key", key,
"result", allowed ? "allowed" : "blocked"
).increment();
}
public void recordLatency(String key, long latency) {
meterRegistry.timer("gateway.rate.limit.latency",
"key", key
).record(Duration.ofMillis(latency));
}
}
完整示例:统一限流管理
限流管理器
@Service
public class UnifiedRateLimiter {
@Autowired
private ReactiveStringRedisTemplate redisTemplate;
/**
* 统一的限流入口
*/
public Mono<RateLimitResult> checkRateLimit(String key, RateLimitType type) {
return switch (type) {
case IP -> checkIpRateLimit(key);
case USER -> checkUserRateLimit(key);
case API -> checkApiRateLimit(key);
case CUSTOM -> checkCustomRateLimit(key);
};
}
private Mono<RateLimitResult> checkIpRateLimit(String ip) {
// IP限流:每秒10次,突发20次
return checkLimit("ip:" + ip, 10, 20, 1);
}
private Mono<RateLimitResult> checkUserRateLimit(String userId) {
// 用户限流:每分钟100次
return checkLimit("user:" + userId, 100, 200, 60);
}
private Mono<RateLimitResult> checkApiRateLimit(String apiKey) {
// API限流:每小时1000次
return checkLimit("api:" + apiKey, 1000, 2000, 3600);
}
private Mono<RateLimitResult> checkCustomRateLimit(String key) {
// 自定义限流
return checkLimit("custom:" + key, 50, 100, 60);
}
private Mono<RateLimitResult> checkLimit(String key, int limit, int burst, int windowSeconds) {
String script = """
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local burst = tonumber(ARGV[2])
local window = tonumber(ARGV[3])
local current = redis.call('INCR', key)
if current == 1 then
redis.call('EXPIRE', key, window)
end
local maxAllowed = (current <= burst) and limit or (limit + (burst - limit) * (window - redis.call('TTL', key)) / window)
if current <= maxAllowed then
return {1, current}
else
return {0, current}
end
""";
return redisTemplate.execute(
new DefaultRedisScript<>(script, List.class),
Arrays.asList(key),
String.valueOf(limit),
String.valueOf(burst),
String.valueOf(windowSeconds)
).map(result -> {
List<Long> values = (List<Long>) result;
return new RateLimitResult(values.get(0) == 1,
values.get(1),
limit,
windowSeconds);
});
}
public record RateLimitResult(boolean allowed, long currentCount, long limit, int windowSeconds) {}
}
// 限流类型枚举
public enum RateLimitType {
IP, USER, API, CUSTOM
}
使用示例
在业务逻辑中使用
@RestController
@RequestMapping("/api")
public class TestController {
@Autowired
private UnifiedRateLimiter rateLimiter;
@GetMapping("/test")
public Mono<ResponseEntity<?>> test() {
return rateLimiter.checkRateLimit("test-key", RateLimitType.CUSTOM)
.flatMap(result -> {
if (result.allowed()) {
return Mono.just(ResponseEntity.ok("请求成功"));
} else {
return Mono.just(
ResponseEntity.status(429)
.header("X-RateLimit-Remaining",
String.valueOf(result.limit() - result.currentCount()))
.body("请求过于频繁")
);
}
});
}
}
这个完整的案例提供了多种限流方案,您可以根据实际需求选择合适的实现方式,主要特性包括:
- 基于Redis的令牌桶限流
- 基于IP、用户、API Key的多维度限流
- 动态规则配置
- 全局和局部限流结合
- 监控和指标收集
- 灵活的错误处理机制