Spring Cloud Gateway 源码剖析:请求过滤、限流熔断与动态路由配置

Spring Cloud Gateway 源码剖析:请求过滤、限流熔断与动态路由配置

各位朋友,大家好!今天我们来深入探讨 Spring Cloud Gateway 的源码,重点分析其核心功能:请求过滤、限流熔断以及动态路由配置。Spring Cloud Gateway 作为 Spring Cloud 生态系统中重要的网关组件,在微服务架构中扮演着至关重要的角色,理解其内部机制对于构建稳定、高效的微服务系统至关重要。

一、Spring Cloud Gateway 架构概览

在深入源码之前,我们先对 Spring Cloud Gateway 的整体架构有个大致的了解。Spring Cloud Gateway 基于 Spring WebFlux 构建,采用 Reactor 响应式编程模型,具有高性能、非阻塞的特点。

核心组件:

  • Gateway Handler Mapping: 负责将请求映射到对应的 RoutePredicateHandlerMapping。
  • RoutePredicateHandlerMapping: 匹配请求路由,并根据匹配结果选择对应的 GatewayFilter。
  • GatewayFilter: 执行具体的过滤逻辑,例如修改请求头、鉴权、限流等。
  • Route: 路由定义,包含 Predicate 和 Filter 列表。
  • Predicate: 断言,用于匹配请求,决定是否应用该路由。
  • GatewayFilterChain: 过滤器链,负责执行一系列 GatewayFilter。
  • WebClient: 用于向后端服务发起请求。

请求处理流程:

  1. 客户端发起请求。
  2. Gateway Handler Mapping 接收到请求,并将其传递给 RoutePredicateHandlerMapping。
  3. RoutePredicateHandlerMapping 根据配置的 Predicate 匹配路由。
  4. 如果匹配成功,则获取该路由对应的 GatewayFilter 列表,构建 GatewayFilterChain。
  5. GatewayFilterChain 依次执行 Filter 链中的每个 GatewayFilter。
  6. 最后一个 Filter 通常是 RouteToRequestUrlFilter,它会将请求转发到后端服务。
  7. 后端服务处理请求并返回响应。
  8. 响应经过 GatewayFilterChain 逆向传递,可以进行一些后置处理。
  9. 最终将响应返回给客户端。

二、请求过滤源码分析

请求过滤是 Spring Cloud Gateway 最基本的功能之一。它允许我们对请求进行各种处理,例如添加请求头、修改请求体、鉴权等。

核心接口:GatewayFilter

GatewayFilter 是所有过滤器的核心接口,定义了 filter 方法,该方法接收 ServerWebExchangeGatewayFilterChain 作为参数。

public interface GatewayFilter extends ShortcutConfigurable {

    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

    // ...省略其他方法
}
  • ServerWebExchange:封装了 HTTP 请求和响应的上下文信息。
  • GatewayFilterChain:过滤器链,负责调用下一个过滤器。

自定义 GatewayFilter

要实现自定义的 GatewayFilter,我们需要实现 GatewayFilter 接口。例如,我们可以创建一个简单的过滤器,用于添加请求头:

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class AddRequestHeaderGatewayFilter implements GatewayFilter {

    private final String headerName;
    private final String headerValue;

    public AddRequestHeaderGatewayFilter(String headerName, String headerValue) {
        this.headerName = headerName;
        this.headerValue = headerValue;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest().mutate()
                .header(headerName, headerValue)
                .build();
        ServerWebExchange mutatedExchange = exchange.mutate()
                .request(request)
                .build();
        return chain.filter(mutatedExchange);
    }
}

在这个例子中,我们创建了一个 AddRequestHeaderGatewayFilter,它接收 headerNameheaderValue 作为参数,并在请求中添加指定的请求头。

GatewayFilter 的应用

application.ymlapplication.properties 中,我们可以配置路由规则,并指定要应用的 GatewayFilter。

spring:
  cloud:
    gateway:
      routes:
        - id: example_route
          uri: http://example.com
          predicates:
            - Path=/example/**
          filters:
            - AddRequestHeader=X-Custom-Header, CustomValue

在这个配置中,我们定义了一个名为 example_route 的路由,它将所有以 /example/** 开头的请求转发到 http://example.com。同时,我们还指定了一个过滤器 AddRequestHeader=X-Custom-Header, CustomValue,它会将 X-Custom-Header 请求头设置为 CustomValue

Spring Cloud Gateway 会自动将配置中的 AddRequestHeader 转换为 AddRequestHeaderGatewayFilter 的实例,并将其添加到过滤器链中。

源码分析:RoutePredicateHandlerMapping

RoutePredicateHandlerMapping 负责匹配请求路由,并根据匹配结果选择对应的 GatewayFilter。其核心方法是 getHandlerInternal

    @Override
    protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
        return lookupRoute(exchange)
                .flatMap(route -> {
                    exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route);
                    return HandlerExecutionChain.just(new GatewayControllerEndpoint(route, this.compositeRouteDefinitionLocator, this.properties.getRoutes()));
                }).cast(HandlerExecutionChain.class)
                .flatMap(handler -> {
                    return applyFilters(exchange, handler);
                }).switchIfEmpty(Mono.empty());
    }
  1. lookupRoute(exchange):根据配置的 Predicate 匹配路由。
  2. applyFilters(exchange, handler):将匹配到的路由对应的 GatewayFilter 列表应用到请求上。

源码分析:GatewayFilterChain

GatewayFilterChain 是过滤器链,负责执行一系列 GatewayFilter。其核心方法是 filter

public interface GatewayFilterChain {
    /**
     * Apply this filter to the current exchange and delegate to the next filter in
     * the chain.
     * @param exchange the current server exchange
     * @return {@code Mono<Void>} to indicate when request handling is complete
     */
    Mono<Void> filter(ServerWebExchange exchange);
}

DefaultGatewayFilterChainGatewayFilterChain 的默认实现,它使用递归的方式依次执行 Filter 链中的每个 GatewayFilter。

三、限流熔断源码分析

限流和熔断是保证微服务系统稳定性的重要手段。Spring Cloud Gateway 提供了内置的限流和熔断机制。

限流:RequestRateLimiterGatewayFilterFactory

RequestRateLimiterGatewayFilterFactory 基于 Redis 或其他 RateLimiter 实现,用于限制单位时间内请求的数量。

核心接口:RateLimiter

RateLimiter 定义了限流器的基本接口,包括 isAllowed 方法,用于判断是否允许请求通过。

public interface RateLimiter<C> {

    /**
     * Configures the rate limiter given the supplied configuration.
     * @param config the configuration for a rate limiter.
     * @return {@link RateLimiter} that has been configured.
     */
    RateLimiter<C> configure(C config);

    /**
     * Determines whether or not access should be allowed based on the configuration and
     * current request.
     * @param routeId the id of the route being accessed.
     * @param exchange the current exchange.
     * @return A {@link Mono} that emits a {@link Response} indicating whether or not
     * access should be allowed.
     */
    Mono<Response> isAllowed(String routeId, String id);

    class Response {

        private final boolean allowed;

        private final Map<String, String> headers;

        public Response(boolean allowed, Map<String, String> headers) {
            this.allowed = allowed;
            this.headers = headers;
        }

        public boolean isAllowed() {
            return allowed;
        }

        public Map<String, String> getHeaders() {
            return headers;
        }

        @Override
        public String toString() {
            return "Response{" + "allowed=" + allowed + ", headers=" + headers + '}';
        }

    }

}

RedisRateLimiter

Spring Cloud Gateway 默认使用 RedisRateLimiter 作为 RateLimiter 的实现。RedisRateLimiter 使用 Redis 的 Lua 脚本来实现原子性的限流操作。

配置限流

application.yml 中,我们可以配置路由规则,并指定要应用的限流器。

spring:
  cloud:
    gateway:
      routes:
        - id: rate_limit_route
          uri: http://example.com
          predicates:
            - Path=/limited/**
          filters:
            - RequestRateLimiter=redis-rate-limiter, 10, 10
      redis:
        host: localhost
        port: 6379

在这个配置中,我们定义了一个名为 rate_limit_route 的路由,它将所有以 /limited/** 开头的请求转发到 http://example.com。同时,我们还指定了一个限流器 RequestRateLimiter=redis-rate-limiter, 10, 10,它使用 redis-rate-limiter 作为 RateLimiter 的名称,允许每秒 10 个请求,令牌桶容量为 10。

熔断:HystrixGatewayFilterFactoryResilience4JGatewayFilterFactory

Spring Cloud Gateway 提供了两种熔断器实现:HystrixGatewayFilterFactoryResilience4JGatewayFilterFactory

  • HystrixGatewayFilterFactory 基于 Netflix Hystrix 实现,已经进入维护模式,不推荐使用。
  • Resilience4JGatewayFilterFactory 基于 Resilience4J 实现,是更现代、更轻量级的选择。

配置熔断

application.yml 中,我们可以配置路由规则,并指定要应用的熔断器。

spring:
  cloud:
    gateway:
      routes:
        - id: resilience4j_route
          uri: http://example.com
          predicates:
            - Path=/resilient/**
          filters:
            - name: Resilience4J
              args:
                name: backendA
                fallbackUri: forward:/fallback

在这个配置中,我们定义了一个名为 resilience4j_route 的路由,它将所有以 /resilient/** 开头的请求转发到 http://example.com。同时,我们还指定了一个熔断器 Resilience4J,它的名称为 backendA,如果后端服务不可用,则会将请求转发到 /fallback

源码分析:Resilience4JGatewayFilterFactory

Resilience4JGatewayFilterFactory 使用 Resilience4J 的 CircuitBreakerRetry 组件来实现熔断和重试功能。

其核心逻辑在于使用 CircuitBreaker 来包装后端服务的调用,当错误率超过阈值时,CircuitBreaker 会打开,阻止后续请求,直到一段时间后尝试恢复。

四、动态路由配置源码分析

动态路由配置允许我们在运行时动态地修改路由规则,而无需重启 Spring Cloud Gateway。Spring Cloud Gateway 提供了多种动态路由配置方式,例如通过 Spring Cloud Config Server、Redis、数据库等。

核心接口:RouteDefinitionLocator

RouteDefinitionLocator 负责从不同的数据源加载路由定义。

public interface RouteDefinitionLocator {

    /**
     * Returns a {@link Flux} of {@link RouteDefinition} objects.
     * @return a flux that emits route definitions
     */
    Flux<RouteDefinition> getRouteDefinitions();

}

内置的 RouteDefinitionLocator 实现

  • PropertiesRouteDefinitionLocator:从 application.ymlapplication.properties 文件中加载路由定义。
  • CompositeRouteDefinitionLocator:组合多个 RouteDefinitionLocator,允许从多个数据源加载路由定义。
  • DiscoveryClientRouteDefinitionLocator:从服务发现组件(例如 Eureka、Consul)中加载路由定义。

自定义 RouteDefinitionLocator

要实现自定义的 RouteDefinitionLocator,我们需要实现 RouteDefinitionLocator 接口,并实现 getRouteDefinitions 方法。例如,我们可以创建一个 RedisRouteDefinitionLocator,用于从 Redis 中加载路由定义。

import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;

@Component
public class RedisRouteDefinitionLocator implements RouteDefinitionLocator {

    private final ReactiveRedisTemplate<String, RouteDefinition> redisTemplate;

    public RedisRouteDefinitionLocator(ReactiveRedisTemplate<String, RouteDefinition> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        return redisTemplate.keys("gateway:route:*")
                .flatMap(redisTemplate.opsForValue()::get);
    }
}

在这个例子中,我们创建了一个 RedisRouteDefinitionLocator,它从 Redis 中加载所有以 gateway:route:* 开头的键对应的路由定义。

动态更新路由

要动态更新路由,我们需要使用 ApplicationEventPublisher 发布 RefreshRoutesEvent 事件。

import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
public class RouteRefresher {

    private final ApplicationEventPublisher publisher;

    public RouteRefresher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void refreshRoutes() {
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }
}

当调用 refreshRoutes 方法时,Spring Cloud Gateway 会重新加载路由定义,并更新路由表。

源码分析:CachingRouteLocator

CachingRouteLocator 是一个缓存路由定位器,它会缓存从 RouteDefinitionLocator 加载的路由定义,以提高性能。

当收到 RefreshRoutesEvent 事件时,CachingRouteLocator 会清除缓存,并重新加载路由定义。

五、核心数据结构:Route 和 RouteDefinition

Spring Cloud Gateway 中有两个重要的数据结构:RouteRouteDefinition

  • RouteDefinition 路由定义,包含路由的元数据信息,例如 ID、URI、Predicate、Filter 等。RouteDefinition 用于配置路由规则。
  • Route 路由实例,是 RouteDefinition 的运行时表示。Route 包含了路由的实际信息,例如匹配的 Predicate、应用的 Filter 列表等。
特性 RouteDefinition Route
作用 路由配置 运行时路由实例
包含信息 ID, URI, Predicate 定义, Filter 定义 ID, URI, Predicate 实例, Filter 实例
生命周期 配置时定义 运行时创建
存储位置 配置文件/数据库/Redis 等 内存中

RouteDefinition 通过 RouteDefinitionRouteLocator 转换为 Route

六、总结

我们深入分析了 Spring Cloud Gateway 的源码,重点探讨了请求过滤、限流熔断和动态路由配置的实现机制。理解这些核心概念和源码,可以帮助我们更好地使用 Spring Cloud Gateway,构建稳定、高效的微服务网关。希望今天的分享对大家有所帮助。

提炼核心,展望未来

Spring Cloud Gateway 通过灵活的过滤器链机制实现了强大的请求处理能力,利用 Redis 等技术实现了有效的限流,并借助 Resilience4J 提供了可靠的熔断机制,同时支持动态路由配置,使得网关具备高度的适应性和可扩展性。 随着云原生技术的不断发展,Spring Cloud Gateway 在微服务架构中的作用将更加重要。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注