JAVA 微服务网关限流不生效?Spring Cloud Gateway Filter 链执行顺序详解
大家好,今天我们来深入探讨一个在微服务架构中经常遇到的问题:Spring Cloud Gateway 的限流配置失效。这个问题看似简单,但背后涉及了 Gateway 的核心机制——Filter 链的执行顺序和配置优先级。我们将从理论到实践,逐步剖析这个问题,并提供有效的解决方案。
1. 理解 Spring Cloud Gateway 的核心概念:Filter 链
Spring Cloud Gateway 的核心在于其强大的路由和过滤功能。当一个请求到达 Gateway 时,它会经过一系列的 Filter 处理,这些 Filter 组成了一个链条,被称为 Filter 链。Filter 链中的每个 Filter 负责特定的任务,例如:
- 请求修改: 修改请求头、请求参数等。
 - 身份验证: 验证用户身份,进行权限控制。
 - 流量控制: 实现限流、熔断等功能。
 - 请求转发: 将请求转发到后端服务。
 - 响应处理: 修改响应头、响应体等。
 
Filter 链的执行顺序至关重要。如果 Filter 的顺序配置不当,可能会导致一些 Filter 没有生效,或者生效的顺序不符合预期,从而导致限流等功能失效。
2. 为什么限流会不生效?常见原因分析
限流不生效的原因有很多,但最常见的可以归结为以下几点:
- Filter 顺序错误: 限流 Filter 没有放在合适的位置,导致请求在限流之前就被转发到后端服务。
 - 配置优先级问题: 不同的配置方式(例如,在 application.yml 中配置和在代码中配置)具有不同的优先级,可能会覆盖限流配置。
 - KeyResolver 问题: 限流的 KeyResolver 没有正确地解析出用于限流的 Key,导致所有请求都被认为是同一个请求,从而无法实现有效的限流。
 - Redis 连接问题: 如果使用 Redis 实现限流,Redis 连接出现问题可能会导致限流失效。
 - 代码逻辑错误: 限流的逻辑存在错误,例如,没有正确地判断是否需要限流,或者没有正确地更新限流计数器。
 
3. 深入 Filter 链的执行顺序:Global Filter vs. Gateway Filter
Spring Cloud Gateway 提供了两种类型的 Filter:
- Global Filter: 全局 Filter,对所有路由都生效。Global Filter 通过实现 
GlobalFilter接口来定义,并可以通过@Order注解或Ordered接口来指定执行顺序。 - Gateway Filter: 路由级别的 Filter,只对特定的路由生效。Gateway Filter 可以在 application.yml 中配置,也可以通过代码方式定义。
 
Global Filter 和 Gateway Filter 的执行顺序是:
- 优先级最高的 Global Filter
 - 路由级别的 Gateway Filter (按照配置顺序执行)
 - 优先级较低的 Global Filter
 
这个顺序非常重要,它决定了哪些 Filter 会先执行,哪些 Filter 会后执行。例如,如果希望在所有路由上进行统一的身份验证,可以将身份验证的 Global Filter 放在优先级最高的位置。
4. 代码示例:配置限流 Filter
我们来演示如何配置一个简单的限流 Filter。这里我们使用 Spring Cloud Gateway 提供的 RequestRateLimiterGatewayFilterFactory 来实现限流。
4.1 添加依赖
首先,确保你的项目中包含了以下依赖:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
4.2 application.yml 配置
spring:
  cloud:
    gateway:
      routes:
        - id: example_route
          uri: http://example.com
          predicates:
            - Path=/example/**
          filters:
            - RequestRateLimiter=redis-rate-limiter,10,10
      redis:
        host: localhost
        port: 6379
redis-rate-limiter:
  replenishRate: 10 # 每秒允许填充的令牌数
  burstCapacity: 20 # 令牌桶的容量
  requestedTokens: 1 # 每次请求消耗的令牌数
在这个配置中,我们定义了一个名为 example_route 的路由,并将所有以 /example/** 开头的请求转发到 http://example.com。同时,我们使用 RequestRateLimiter Filter 对该路由进行限流。
redis-rate-limiter指的是一个BeanName,具体配置在下面10指的是replenishRate,每秒填充的令牌数。10指的是burstCapacity,令牌桶的容量。
4.3 RedisRateLimiter 配置类
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import java.util.Objects;
@Configuration
public class RateLimiterConfig {
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress());
    }
    @Bean
    public RedisRateLimiter redisRateLimiter() {
        return new RedisRateLimiter(1, 1); // 默认配置,可以在 application.yml 中覆盖
    }
}
在这个配置类中,我们定义了两个 Bean:
ipKeyResolver:用于解析限流的 Key,这里我们使用客户端的 IP 地址作为 Key。redisRateLimiter:RedisRateLimiter 的默认配置,可以在 application.yml 中通过spring.cloud.gateway.redis-rate-limiter进行覆盖。
5. 常见问题及解决方案
5.1 限流 Filter 没有生效
- 检查 Filter 顺序: 确保限流 Filter 在身份验证 Filter 之后,但在请求转发 Filter 之前。
 - 检查配置优先级: 确保你的限流配置没有被其他配置覆盖。
 - 检查 KeyResolver: 确保 KeyResolver 正确地解析出用于限流的 Key。
 - 检查 Redis 连接: 确保 Redis 连接正常。
 
5.2 所有请求都被限流
- 检查 KeyResolver: 检查 KeyResolver 是否返回了相同的值,导致所有请求都被认为是同一个请求。
 - 检查限流配置: 检查限流的 
replenishRate和burstCapacity是否设置得过小。 
5.3 部分请求没有被限流
- 检查 KeyResolver: 检查 KeyResolver 是否正确地解析出不同的 Key,导致部分请求没有被限流。
 - 检查限流逻辑: 检查限流的逻辑是否存在错误,例如,没有正确地判断是否需要限流。
 
6. 高级技巧:自定义限流 Filter
除了使用 Spring Cloud Gateway 提供的 RequestRateLimiterGatewayFilterFactory,我们还可以自定义限流 Filter,以满足更复杂的需求。
6.1 实现 GatewayFilterFactory 接口
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class CustomRateLimiterGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomRateLimiterGatewayFilterFactory.Config> {
    private final AtomicInteger counter = new AtomicInteger(0);
    public CustomRateLimiterGatewayFilterFactory() {
        super(Config.class);
    }
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (counter.incrementAndGet() > config.getLimit()) {
                exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange).doFinally(signalType -> counter.decrementAndGet());
        };
    }
    public static class Config {
        private int limit;
        public int getLimit() {
            return limit;
        }
        public void setLimit(int limit) {
            this.limit = limit;
        }
    }
}
在这个示例中,我们定义了一个名为 CustomRateLimiterGatewayFilterFactory 的自定义 Filter,它继承了 AbstractGatewayFilterFactory 接口。该 Filter 使用一个 AtomicInteger 来记录请求数量,如果请求数量超过了配置的 limit,则返回 429 Too Many Requests 错误。
6.2 application.yml 配置
spring:
  cloud:
    gateway:
      routes:
        - id: example_route
          uri: http://example.com
          predicates:
            - Path=/example/**
          filters:
            - CustomRateLimiter=5
在这个配置中,我们使用 CustomRateLimiter Filter 对该路由进行限流,并将 limit 设置为 5。
7. Spring Cloud Gateway Filter 链执行顺序总结
| 执行顺序 | Filter 类型 | 说明 | 
|---|---|---|
| 1 | Global Filter (最高优先级) | 对所有路由生效,执行顺序由 @Order 注解或 Ordered 接口指定。 | 
| 2 | Gateway Filter | 路由级别的 Filter,只对特定的路由生效,执行顺序由 application.yml 中的配置顺序决定。 | 
| 3 | Global Filter (较低优先级) | 对所有路由生效,执行顺序由 @Order 注解或 Ordered 接口指定。 | 
8. 理解和避免限流失效,保障服务稳定
通过以上分析,我们可以看到,Spring Cloud Gateway 的限流配置失效往往是由于 Filter 链的执行顺序和配置优先级问题导致的。要解决这个问题,我们需要深入理解 Gateway 的核心机制,仔细检查 Filter 的顺序和配置,并根据实际需求选择合适的限流策略。只有这样,才能确保限流功能正常工作,保障微服务的稳定性和可用性。
9. 深入理解核心机制,排查问题更加高效
Spring Cloud Gateway 的 Filter 链执行顺序是理解限流是否生效的关键。Global Filter 和 Gateway Filter 的优先级和顺序,以及 KeyResolver 的正确配置,都会直接影响限流效果。记住,仔细分析配置和代码,能够更有效地排查问题。
10. 自定义 Filter 提供更高的灵活性
虽然 Spring Cloud Gateway 提供了默认的限流 Filter,但自定义 Filter 能够满足更复杂的需求。通过实现 GatewayFilterFactory 接口,可以灵活地控制限流逻辑,并与其他服务集成,实现更强大的功能。