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


请求处理核心源码流程
以下源码基于 spring-cloud-starter-gateway : 2.2.10.RELEASE
- 1、请求入口:
ReactorHttpHandlerAdapter
- Spring Cloud Gateway 底层网络框架使用的是
Netty
,ReactorHttpHandlerAdapter
类接收网络请求后,将Netty
请求和响应对象转换为标准的 HTTP 请求和响应对象,然后交给 HTTP handler完成后续逻辑处理。 ReactorHttpHandlerAdapter
其实是 Spring Webflux 中的一个类,用于将HttpHandler
接口适配为 Reactor Netty 所需的类型,从而使得基于 Spring Webflux 的应用程序能够利用 Reactor Netty 作为其服务器层。它在 Spring Webflux 和 Reactor Netty 之间充当了一座桥梁,允许使用 Spring 的编程模型(如函数式端点和注解驱动的控制器)与 Reactor Netty 高性能非阻塞服务器结合使用。
- Spring Cloud Gateway 底层网络框架使用的是
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
HttpWebHandlerAdapter
(HttpHandler
的实现类)把请求和响应上下文封装为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
)可以将请求转发到正确的服务实例。
- 基于服务发现和负载均衡器选择服务实例并更新请求的 URL,以便后续过滤器(如
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
返回给客户端。
源码核心处理流程总结:
