Spring Cloud Gateway 核心原理(含源码分析)

公众号:JavaArchJourney

Spring Cloud Gateway 是 Spring 官方推出的一个用于构建 API 网关的库,旨在为微服务架构提供一种简单而有效的统一访问入口。

主要特性

  • 非阻塞响应式IO:Spring Cloud Gateway 是基于 Spring WebFlux 框架构建的,而 Spring WebFlux 默认使用的是 Netty 服务器。基于 Netty 的 I/O 多路复用和事件响应机制来实现网络通信,这种非阻塞的响应式 IO 可以极大提高网络吞吐量。
  • 路由定义配置 :支持 Java 编码或在 application.yml 文件中定义 HTTP 请求的路由规则。
  • 过滤器支持:在发送下游请求之前或之后可以应用一系列过滤器,以实现如重试机制、修改请求/响应头等操作。
  • 动态路由:支持根据服务发现机制动态调整路由,这使得它可以很好地适应于微服务架构中的服务实例变化。
  • 断路器集成:与 Spring Cloud Netflix Hystrix 集成,可以在请求失败时执行熔断保护或降级逻辑,从而提高系统的容错性。
  • 限流:支持对进入网关的请求进行速率限制,有助于保护后端服务免受突发流量的影响。
  • 安全功能:支持基本的安全功能,如认证和授权,确保只有经过验证的请求才能到达后端服务。

简单使用示例

场景:创建一个基本的网关应用程序,把请求路由到不同的后端服务。

pom.xml 添加依赖:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>

application.yml 文件中定义路由规则:假设我们有两个服务:service1 和 service2,它们分别运行在 localhost:8081localhost:8082 上。我们希望所有对 /service1/** 的请求都被转发到 service1,而对 /service2/** 的请求被转发到 service2。

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: service1_route
        uri: http://localhost:8081/
        predicates:
        - Path=/service1/**
      - id: service2_route
        uri: http://localhost:8082/
        predicates:
        - Path=/service2/**

或者也可以使用类配置:

java 复制代码
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                // 定义 service1 的路由
                .route("service1_route", r -> r.path("/service1/**")
                        .uri("http://localhost:8081/"))
                // 定义 service2 的路由
                .route("service2_route", r -> r.path("/service2/**")
                        .uri("http://localhost:8082/"))
                .build();
    }
}

完成上述步骤后,启动应用程序,尝试访问 http://localhost:8080/service1/your-endpointhttp://localhost:8080/service2/your-endpoint,就能看到请求被正确地路由到了对应的后端服务。

核心工作原理

Spring Cloud Gateway 的核心是其路由器和过滤器的组合。当一个请求到达网关时,首先会通过路由匹配过程确定应该将请求转发到哪个服务。一旦路由被确定,请求就会穿过一系列的过滤器,这些过滤器可以修改请求或响应的内容,或者执行一些额外的操作,比如记录日志、增加头部信息等。

核心组件

  • Route(路由) :路由是网关的基本构建块。每个路由都包含一个 ID、目标 URI、一组断言(Predicates)和过滤器(Filters)。断言决定了请求是否匹配该路由,而过滤器则可以修改请求或响应。
  • Predicate(断言)Predicate 来自 Java 8 的接口,用来决定请求是否应该被路由到指定的服务。它们通常基于 HTTP 请求的不同属性,如路径、HTTP 方法、请求参数等。Spring Cloud Gateway 提供了多种内置断言工厂,也可以自定义断言。
  • Filter(过滤器):过滤器可以在请求到达目标服务之前( Pre-Filters )或之后( Post-Filters )对请求进行处理。Spring Cloud Gateway 支持全局过滤器和特定于路由的过滤器。过滤器可用于实现日志记录、请求/响应头修改、限流等功能。

请求处理的逻辑流程

  1. 路由判断:当客户端发起请求至网关时,首先会由 Gateway Handler Mapping 组件进行处理。此过程涉及使用断言(Predicate)来确定最适合当前请求的路由规则,进而将其映射到对应的后端服务上。
  2. 请求过滤:接下来,请求会被转交给 Gateway Web Handler,这里面有很多过滤器,组成过滤器链(Filter Chain)。这些过滤器能够对请求进行多种操作,如添加或修改请求头、执行参数校验等。这一阶段处理的逻辑就是 Pre-Filters(前置过滤),即在请求被转发至实际后端服务前进行的处理步骤。
  3. 服务处理:请求经过了必要的预处理后,会被转发给相应的后端服务进行业务逻辑处理。
  4. 响应过滤:后端服务完成处理并将结果返回给网关后,过滤器链的 Post-Filters(后置过滤)开始发挥作用。这些过滤器会对从后端服务收到的响应进行进一步处理,比如数据格式调整、敏感信息过滤等操作。
  5. 响应返回:最终,经过所有必要处理的响应会返回给客户端,完成整个交互过程。

请求处理核心源码流程

以下源码基于 spring-cloud-starter-gateway : 2.2.10.RELEASE

  • 1、请求入口: ReactorHttpHandlerAdapter
    • Spring Cloud Gateway 底层网络框架使用的是 NettyReactorHttpHandlerAdapter 类接收网络请求后,将 Netty 请求和响应对象转换为标准的 HTTP 请求和响应对象,然后交给 HTTP handler完成后续逻辑处理。
    • ReactorHttpHandlerAdapter 其实是 Spring Webflux 中的一个类,用于将 HttpHandler 接口适配为 Reactor Netty 所需的类型,从而使得基于 Spring Webflux 的应用程序能够利用 Reactor Netty 作为其服务器层。它在 Spring Webflux 和 Reactor Netty 之间充当了一座桥梁,允许使用 Spring 的编程模型(如函数式端点和注解驱动的控制器)与 Reactor Netty 高性能非阻塞服务器结合使用。
java 复制代码
public Mono<Void> apply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) {
    NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(reactorResponse.alloc());
    try {
        // 将 Netty 的请求包装成 Spring WebFlux 的请求对象
        ReactorServerHttpRequest request = new ReactorServerHttpRequest(reactorRequest, bufferFactory);
        // 将 Netty 的响应包装成 Spring WebFlux 的响应对象
        ServerHttpResponse response = new ReactorServerHttpResponse(reactorResponse, bufferFactory);

        // (省略)...

        // 调用 httpHandler 的 handle 方法,处理请求并返回 Mono<Void>
        return this.httpHandler
                .handle(request, (ServerHttpResponse) response)
                .doOnError((ex) -> {
                    logger.trace(request.getLogPrefix() + "Failed to complete: " + ex.getMessage());
                })
                .doOnSuccess((aVoid) -> {
                    logger.trace(request.getLogPrefix() + "Handling completed");
                });
    } catch (URISyntaxException var6) {
        if (logger.isDebugEnabled()) {
            logger.debug("Failed to get request URI: " + var6.getMessage());
        }
        // 设置响应状态为400(Bad Request)
        reactorResponse.status(HttpResponseStatus.BAD_REQUEST);
        // 返回空的Mono
        return Mono.empty();
    }
}
  • **2、请求封装为 **ServerWebExchange
    • HttpWebHandlerAdapterHttpHandler 的实现类)把请求和响应上下文封装为 ServerWebExchange 对象
java 复制代码
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
    // (省略)...
    
    // ServerWebExchange 是整个请求处理的核心上下文对象,包含请求、响应、属性等信息。
    ServerWebExchange exchange = this.createExchange(request, response);
    // getDelegate().handle() 实际上是调用 DispatcherHandler.handle()
    return this.getDelegate().handle(exchange)
        .doOnSuccess(aVoid -> logResponse(exchange))
        .onErrorResume(ex -> handleUnresolvedError(exchange, ex))
        .then(Mono.defer(response::setComplete));
}
  • 3、请求分发: DispatcherHandler
    • 遍历所有 HandlerMapping 实例,找到匹配的 Handler 处理器,并执行;若找不到则通常最后会返回 404 错误。
java 复制代码
public Mono<Void> handle(ServerWebExchange exchange) {
    // (省略)...
    
    // 遍历所有 HandlerMapping 实例,调用 getHandler(exchange) 查找匹配的处理器
    return Flux.fromIterable(this.handlerMappings)
        .concatMap(mapping -> mapping.getHandler(exchange))
        .next()
        .switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION))
        // 找到第一个匹配的处理器后,调用 invokeHandler() 执行处理器逻辑
        .flatMap(handler -> invokeHandler(exchange, handler))
        .flatMap(result -> handleResult(exchange, result));
}
  • 4、路由匹配: RoutePredicateHandlerMapping
    • RoutePredicateHandlerMapping 是实现了 HandlerMapping 接口的一个实例。它根据配置的路由规则,使用 Predicate 断言来评估哪个路由与当前请求匹配。如果找到匹配项,则会返回 FilteringWebHandler 用于后续的过滤器链处理流程。
java 复制代码
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
    //(忽略)...

    // 查找匹配的路由
    return lookupRoute(exchange)
            // 如果找到匹配的路由,执行以下操作
            .flatMap((Function<Route, Mono<?>>) r -> {
                // 从exchange属性中移除旧的路由断言结果
                exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                // 将匹配的路由存入exchange属性中
                exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);

                // 返回 webHandler(FilteringWebHandler),负责后续的过滤器链执行
                return Mono.just(webHandler);
            })
            // 如果没有找到匹配的路由,执行以下默认行为
            .switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
                // 移除旧的路由断言结果
                exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                // 最终结果返回空的 Mono,表示没有处理器能处理这个请求(通常会返回 404)
            })));
}

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
    return this.routeLocator
            // 获取所有的路由定义(Flux<Route>)
            .getRoutes()
            // 对每一个路由依次评估其断言(predicate)是否满足当前请求
            .concatMap(route -> Mono.just(route).filterWhen(r -> {
                exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
                return r.getPredicate().apply(exchange);
            })
                    // 错误处理(忽略错误,继续尝试其他路由)
                    .doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
                    .onErrorResume(e -> Mono.empty()))
            // 取第一个匹配成功的路由
            .next()
            // 校验路由(默认为空实现,可用于扩展)
            .map(route -> {
                validateRoute(route, exchange);
                return route;
            });
}
  • 5、执行过滤器链: FilteringWebHandler
    • 构建并执行过滤器链,包括全局过滤器和针对此路由的特定过滤器。
    • 过滤器中可以修改请求或响应,甚至短路整个请求过程。
    • 典型的过滤器包括:
      • LoadBalancerClientFilter:实现服务发现与负载均衡。
      • AdaptCachedBodyGlobalFilter:处理请求体缓存。
      • RewritePathGatewayFilter:重写请求路径。
      • ForwardRoutingFilter:将请求转发到本机目标服务实例。
      • NettyRoutingFilter:将请求通过网络转发到下游目标服务实例。
    • 所有过滤器链执行结束后,会调用 exchange.getResponse().setComplete() 结束请求。
java 复制代码
// FilteringWebHandler.handle:
public Mono<Void> handle(ServerWebExchange exchange) {
    // (省略)...
    
    // 合并全局过滤器(GlobalFilter)和局部过滤器(GatewayFilter):
    // 获取当前路由相关的GatewayFilter列表
    List<GatewayFilter> gatewayFilters = route.getFilters();
    // 创建一个新的列表,首先添加全局过滤器,然后添加路由级别的过滤器
    List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
    combined.addAll(gatewayFilters);
    
    // 对过滤器进行排序,确保它们按照正确的顺序执行
    AnnotationAwareOrderComparator.sort(combined);

    // 通过 DefaultGatewayFilterChain 执行过滤器链
    return new DefaultGatewayFilterChain(combined).filter(exchange);
}

// DefaultGatewayFilterChain.filter:
public Mono<Void> filter(ServerWebExchange exchange) {
        // 创建一个延迟执行的 Mono<Void>,避免在链创建时立即执行,而是等到有订阅者时才触发执行
        return Mono.defer(() -> {
            // 遍历过滤器链中的所有过滤器,执行器 filter 方法
            if (this.index < filters.size()) {
                GatewayFilter filter = filters.get(this.index);
                DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);
                return filter.filter(exchange, chain);
            } else {
                // 过滤器链执行结束
                return Mono.empty(); // complete
            }
        });
}
  • 6、服务发现与负载均衡: ReactiveLoadBalancerClientFilter
    • 基于服务发现和负载均衡器选择服务实例并更新请求的 URL,以便后续过滤器(如 ForwardRoutingFilter / NettyRoutingFilter )可以将请求转发到正确的服务实例。
java 复制代码
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    // 获取当前请求的目标URL
    URI url = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
    // 获取scheme前缀属性,用于支持特定场景下的路由规则
    String schemePrefix = (String)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
    // 检查URL是否有效且其scheme为"lb"(表示负载均衡),或schemePrefix为"lb"
    if (url != null && ("lb".equals(url.getScheme()) || "lb".equals(schemePrefix))) {
        // (省略)...
    
        // 调用choose方法选择一个服务实例
        return this.choose(exchange).doOnNext((response) -> {
            // 如果没有找到服务实例,则抛出NotFoundException
            if (!response.hasServer()) {
                throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());
            } else {
                // 获取选定的服务实例
                ServiceInstance retrievedInstance = (ServiceInstance)response.getServer();
                // 获取当前请求的原始 URI(比如:lb://service-name/api/resource)
                URI uri = exchange.getRequest().getURI();
                
                // (省略)...

                // 构建新的 URI,替换原始 URI 的 host 和 port 为选中实例的值(比如:将 lb://service-name/api/resource 替换为 http://192.168.1.10:8080/api/resource)
                URI requestUrl = this.reconstructURI(serviceInstance, uri);
                // 更新交换中的请求URL属性
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
            }
        })
        // 继续执行过滤器链,将请求转发到新的 URI。
        .then(chain.filter(exchange));
    } else {
        // 如果不满足条件,直接调用下一个过滤器
        return chain.filter(exchange);
    }
}
  • 7、请求转发: ForwardRoutingFilter / NettyRoutingFilter
    • ForwardRoutingFilter:将请求转发到本地 Spring MVC 控制器。通过这种方式使得 Spring Cloud Gateway 能够不仅可以作为请求的路由器,还可以作为一个功能完整的Web应用程序来处理特定的请求路径或模式。
    • NettyRoutingFilter:将请求通过网络转发到下游目标服务实例。
java 复制代码
public class ForwardRoutingFilter implements GlobalFilter, Ordered {
    // (省略)...
    
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取路由URL属性,这个属性包含了请求的目标地址
        URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        // 获取目标地址的scheme,例如 "forward", "http", "https"
        String scheme = requestUrl.getScheme();
        // 如果请求未路由过,且scheme为"forward"
        if (!ServerWebExchangeUtils.isAlreadyRouted(exchange) && "forward".equals(scheme)) {
            // 使用DispatcherHandler来处理请求,DispatcherHandler负责将请求分发给Spring MVC控制器
            return this.getDispatcherHandler().handle(exchange);
        } else {
            // 如果不满足上述条件,则继续执行下一个过滤器
            return chain.filter(exchange);
        }
    }
}
java 复制代码
public class NettyRoutingFilter implements GlobalFilter, Ordered {
    // (省略)...
    
    // 利用 Spring WebFlux 和 Netty 提供的非阻塞 I/O 能力来高效地转发请求并处理响应:
    // 通过使用 HttpClient 异步地将请求转发给下游微服务,并在接收到响应后,将响应数据返回给原始请求者。
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // (省略)...

        // 通过 Netty 客户端 HttpClient 发起请求到下游服务,并处理响应。这里包括了设置请求头、发送请求体以及处理响应等步骤
        Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange)
                .headers( ... ) // 设置请求头(含 Host 控制)
                .request(method).uri(url) // 指定请求方法和目标 URL
                .send( ... ) // 发送原始请求体(body)
                .responseConnection((res, connection) -> {
                    // ... 处理下游响应,包括状态码、响应头、过滤等
                    return Mono.just(res);
                });

        // 超时处理:如果设置了响应超时时间,则应用超时策略
        Duration responseTimeout = getResponseTimeout(route);
        if (responseTimeout != null) {
            responseFlux = responseFlux
                    .timeout(responseTimeout, Mono.error(new TimeoutException(
                            "Response took longer than timeout: " + responseTimeout)))
                    .onErrorMap(TimeoutException.class,
                            th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT,
                                    th.getMessage(), th));
        }

        return responseFlux.then(chain.filter(exchange));
    }
}
  • 8、响应返回
    • 目标服务处理完成后,响应返回到网关。
    • 响应经过过滤器链的 Post-Filter 阶段(如日志记录、响应头修改)。
    • 最终通过 ServerHttpResponse 返回给客户端。

源码核心处理流程总结:

参考

相关推荐
MrSYJ3 小时前
AuthenticationEntryPoint认证入口
java·spring cloud·架构
银迢迢7 小时前
SpringCloud微服务技术自用笔记
java·spring cloud·微服务·gateway·sentinel
弈芯1 天前
SpringCloud微服务拆分最佳实践
spring cloud
麦兜*1 天前
【Prometheus】 + Grafana构建【Redis】智能监控告警体系
java·spring boot·redis·spring·spring cloud·grafana·prometheus
sniper_fandc2 天前
Spring Cloud系列—SkyWalking告警和飞书接入
spring cloud·skywalking
abigalexy3 天前
深入图解Spring Cloud底层设计
spring·spring cloud
楠有枝4 天前
普通用户使用docker命令
spring cloud·docker·eureka
孤狼程序员4 天前
【Spring Cloud 微服务】2.守护神网关Gateway
spring cloud·微服务·gateway
朱皮皮呀5 天前
Spring Cloud——服务注册与服务发现原理与实现
运维·spring cloud·eureka·服务发现·php
朱皮皮呀5 天前
微服务流量分发核心:Spring Cloud 负载均衡解析
spring cloud·微服务·负载均衡