一、背景
Spring Cloud Gateway
作为一种微服务网关组件,相信大家都不陌生,一个请求经过Spring Cloud Gateway
是如何转发出去的,今天我们就来分析一下这部分的源码。
二、正文
下面这张图大家在学习Spring Cloud Gateway
的时候肯定见过,在分析源码之前我们再来看下这张图。
其中
- Gateway Client: 发送请求到 Spring Cloud Gateway 的客户端
- Gateway Handler Mapping: 是处理请求的组件,负责将请求映射到相应的处理器。处理请求将被路由到哪个路由规则,从而选择对应的过滤器链
- Gateway Web Handler: 实际处理请求的组件,会依次执行过滤器链,对请求进行处理
- Gateway Filter: 过滤器链由多个过滤器组成,每个过滤器执行一些特定代码逻辑
- Proxied Service: 被代理的服务,当执行完过滤器链之后会将请求转发到具体的目标服务
官网上有这样一段解释:
📌Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a route, it is sent to the Gateway Web Handler. This handler runs the request through a filter chain that is specific to the request。
客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 确定请求与路由匹配,则会将其发送到 Gateway Web Handler。 此 handler 通过一个特定于请求的过滤器链来运行请求(这个处理程序会让请求通过一系列只为这个请求设计的过滤器。简单来说,就是请求会经过一组专门为它设置的过滤器。
我们先来看下 gateway 的自动配置类 GatewayAutoConfiguration
Gateway的自动配置
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnProperty(
name = {"spring.cloud.gateway.enabled"},
matchIfMissing = true
)
@EnableConfigurationProperties
//自动配置前置条件:引入了WebFlux 和 HttpHandler 组件
@AutoConfigureBefore({HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class})
//自动配置后置组件:负载均衡组件
@AutoConfigureAfter({GatewayReactiveLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass({DispatcherHandler.class})
public class GatewayAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
return new PropertiesRouteDefinitionLocator(properties);
}
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
}
@Bean
@ConditionalOnMissingBean
public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);
}
@Bean
public GatewayProperties gatewayProperties() {
return new GatewayProperties();
}
@Bean
@ConditionalOnEnabledGlobalFilter
public RouteToRequestUrlFilter routeToRequestUrlFilter() {
return new RouteToRequestUrlFilter();
}
/**
* GlobalFilter自动注入使用的是 @Autowired,不需要显式写出,spring 会自动查找所有类型为 GlobalFilter 的 Bean,并将它们注入到这个List中
*/
@Bean
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
return new FilteringWebHandler(globalFilters);
}
......
}
自动配置类GatewayAutoConfiguration
在内部初始化了很多bean,列举几个重要的如下:
-
PropertiesRouteDefinitionLocator
:用于从配置文件(yml/properties)中读取路由配置信息 -
RouteDefinitionLocator
:把 RouteDefinition 转化为 Route -
RoutePredicateHandlerMapping
:类似于 mvc 的HandlerMapping,用于匹配对应的请求 Route -
GatewayProperties
:yml配置信息封装在 GatewayProperties 对象中 -
GlobalFilter
实现类:比如RouteToRequestUrlFilter、ForwardRoutingFilter等全局过滤器 -
FilteringWebHandler
:注入了所有实现了 GlobalFilter 接口的过滤器类
FilteringWebHandler
中注入的GlobalFilter如下:
{
"org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@77856cc5": 10100,
"org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@4f6fd101": 10000,
"org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@32d22650": -1,
"org.springframework.cloud.gateway.filter.ForwardRoutingFilter@106459d9": 2147483647,
"org.springframework.cloud.gateway.filter.NettyRoutingFilter@1fbd5e0": 2147483647,
"org.springframework.cloud.gateway.filter.ForwardPathFilter@33a71d23": 0,
"org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@135064ea": 2147483637,
"org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@23c05889": 2147483646
}
Gateway的工作流程
- 所有请求都将由
ReactorHttpHandlerAdapter.apply()
方法拦截处理,此时会封装请求对象和响应对象,并传递到HttpWebHandlerAdapter.handle()
方法。 HttpWebHandlerAdapter.handle()
方法将request和response封装成上下文对象ServerWebExchange
,方法通过getDelegate()
获取全局异常处理器ExceptionHandlingWebHandler
执行全局异常处理
这两步出现的类是在 spring-web 包下
ExceptionHandlingWebHandler
执行完成后,调用DispatcherHandler.handle()
,循环所有handlerMappings
查找处理当前请求的HandlerDispatcherHandler
在
spring-webflux
包下,我们知道Gateway
是基于webflux
响应式编程
- 找到
Handler
后调用DispatcherHandler.invokeHandler()
执行找到的Handler
,此时会调用FilteringWebHandler.handle()
DefaultGatewayFilterChain.filter()
是关键流程,所有过滤器都会在这里执行,比如服务查找、负载均衡、远程调用等,都在这一块。
Gateway源码分析
📌Spring Cloud Gateway版本:spring-cloud-gateway 3.1.4
我们直接从HttpWebHandlerAdapter.handle()
看起
HttpWebHandlerAdapter
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
if (this.forwardedHeaderTransformer != null) {
try {
request = this.forwardedHeaderTransformer.apply(request);
} catch (Throwable var4) {
Throwable ex = var4;
if (logger.isDebugEnabled()) {
logger.debug("Failed to apply forwarded headers to " + this.formatRequest(request), ex);
}
response.setStatusCode(HttpStatus.BAD_REQUEST);
return response.setComplete();
}
}
// 创建网关上下文对象
ServerWebExchange exchange = this.createExchange(request, response);
LogFormatUtils.traceDebug(logger, (traceOn) -> {
return exchange.getLogPrefix() + this.formatRequest(exchange.getRequest()) + (traceOn ? ", headers=" + this.formatHeaders(exchange.getRequest().getHeaders()) : "");
});
// getDelegate()获取当前的Handler
Mono var10000 = this.getDelegate().handle(exchange).doOnSuccess((aVoid) -> {
this.logResponse(exchange);
}).onErrorResume((exx) -> {
return this.handleUnresolvedError(exchange, exx);
});
response.getClass();
return var10000.then(Mono.defer(response::setComplete));
}
代码中先创建了网关上下文对象ServerWebExchange
,然后getDelegate()
获取当前的Handler
,我们将断点打到getDelegate()
方法里面:
当前返回的 WebHandler 是ExceptionHandlingWebHandler
,而ExceptionHandlingWebHandler
的 delegate 是FilteringWebHandler
,FilteringWebHandler
的 delegate 是DispatcherHandler
,所有的 delegate 的handle()
方法都会依次执行。
delegate: 委托、委派 这里应该是使用了委派模式,不属于23种设计模式,类似代理模式,委派模式主要关注任务的调度和分配,注重结果,可以看作是一种特殊的静态代理,即全权代理。
接下来我们可以把断点放到DispatcherHandler.handler()
方法上
DispatcherHandler
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return this.createNotFoundError();
} else {
// 1.遍历所有的handlerMapping,获取到当前网关上下文中的多个handlerMapping
return CorsUtils.isPreFlightRequest(exchange.getRequest()) ? this.handlePreFlight(exchange) : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
return mapping.getHandler(exchange);
// 2.获取对应的适配器,调用对应的处理器
}).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
return this.invokeHandler(exchange, handler);
// 3. 返回处理结果
}).flatMap((result) -> {
return this.handleResult(exchange, result);
});
}
}
可以看到这里所有的handlerMapping
,方法中会调用所有 handlerMappings
的getHandler(exchange)
方法,
点进去getHandler(exchange)
方法进入一个抽象类AbstractHandlerMapping
AbstractHandlerMapping
public Mono<Object> getHandler(ServerWebExchange exchange) {
return this.getHandlerInternal(exchange).map((handler) -> {
if (this.logger.isDebugEnabled()) {
this.logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
}
ServerHttpRequest request = exchange.getRequest();
if (this.hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null;
CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, exchange);
config = config != null ? config.combine(handlerConfig) : handlerConfig;
if (config != null) {
config.validateAllowCredentials();
}
if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
return NO_OP_HANDLER;
}
}
return handler;
});
}
类中的getHandler(exchange)
方法会调getHandlerInternal(exchange)
方法
可以看到getHandlerInternal(exchange)
该方法由各个HandlerMapping
自行实现,这里有6个重写了getHandlerInternal
方法的类,
由于是网关组件,当请求进入时,会先判断路由,所以会进入实现类RoutePredicateHandlerMapping
中。
RoutePredicateHandlerMapping
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
if (this.managementPortType == RoutePredicateHandlerMapping.ManagementPortType.DIFFERENT && this.managementPort != null && exchange.getRequest().getURI().getPort() == this.managementPort) {
return Mono.empty();
} else {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR, this.getSimpleName());
return this.lookupRoute(exchange).flatMap((r) -> {
exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Mapping [" + this.getExchangeDesc(exchange) + "] to " + r);
}
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r);
// 此处的webhandler是FilteringWebhandler
return Mono.just(this.webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No RouteDefinition found for [" + this.getExchangeDesc(exchange) + "]");
}
})));
}
}
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes().concatMap((route) -> {
return Mono.just(route).filterWhen((r) -> {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return (Publisher)r.getPredicate().apply(exchange);
}).doOnError((e) -> {
this.logger.error("Error applying predicate for route: " + route.getId(), e);
}).onErrorResume((e) -> {
return Mono.empty();
});
}).next().map((route) -> {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Route matched: " + route.getId());
}
this.validateRoute(route, exchange);
return route;
});
}
lookupRoute
中的this.routeLocator.getRoutes()
会通过RouteDefinitionRouteLocator
拿到yml配置文件中所有的路由断言工厂(Before、After、Path等等
),然后把找到的路由转换成Route
,再执行apply方法,进行路由匹配,判断是否允许请求通过。
找到对应Route后会返回指定的FilterWebHandler,如下代码:
至此对应着DispatcherHandler
中的第一步获取到当前网关上下文中的多个handlerMapping
.
接下来我们看DispatcherHandler
中的第二步获取对应的适配器,调用对应的处理器.
进入获取对应的适配器方法 invokeHandler(exchange, handler)
中
请求对应的适配器是SimpleHandlerAdapter
SimpleHandlerAdapter
public class SimpleHandlerAdapter implements HandlerAdapter {
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
WebHandler webHandler = (WebHandler)handler;
Mono<Void> mono = webHandler.handle(exchange);
return mono.then(Mono.empty());
}
}
这里调用了FilteringWebHandler
的handle()
方法
FilteringWebHandler
public Mono<Void> handle(ServerWebExchange exchange) {
Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
List<GatewayFilter> gatewayFilters = route.getFilters();
List<GatewayFilter> combined = new ArrayList(this.globalFilters);
combined.addAll(gatewayFilters);
AnnotationAwareOrderComparator.sort(combined);
if (logger.isDebugEnabled()) {
logger.debug("Sorted gatewayFilterFactories: " + combined);
}
return (new DefaultGatewayFilterChain(combined)).filter(exchange);
}
这里面组装了所有使用到的过滤器,使用责任链的设计模式去实现调用,过滤器按照一定顺序排序,order值越小越先执行,
我们看RouteToRequestUrlFilter
和ForwardRoutingFilter
过滤器.
RouteToRequestUrlFilter
:路由转换路由转换,把http://localhost:8888/order/test0
—> lb://mall-order/order/test0
ForwardRoutingFilter
:转发路由网关过滤器。其根据 forward:// 前缀( Scheme )过滤处理,将请求转发到当前网关实例本地接口。
RouteToRequestUrlFilter
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 从exchange中取路由Route对象
Route route = (Route)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
if (route == null) {
return chain.filter(exchange);
} else {
log.trace("RouteToRequestUrlFilter start");
// 取当前请求uri:http://localhost:8888/order/test0
URI uri = exchange.getRequest().getURI();
boolean encoded = ServerWebExchangeUtils.containsEncodedParts(uri);
// 路由对象中保存的uri,也就是我们在yml文件中配置的值: lb://mall-order
URI routeUri = route.getUri();
if (hasAnotherScheme(routeUri)) {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());
routeUri = URI.create(routeUri.getSchemeSpecificPart());
}
if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {
throw new IllegalStateException("Invalid host: " + routeUri.toString());
} else {
// 转换结果为:lb://mall-order/order/test0
URI mergedUrl = UriComponentsBuilder.fromUri(uri).scheme(routeUri.getScheme()).host(routeUri.getHost()).port(routeUri.getPort()).build(encoded).toUri();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, mergedUrl);
return chain.filter(exchange);
}
}
}
再往后就是通过NettyRoutingFilter
发起远程调用
NettyRoutingFilter
SpringCloud在实现对后端服务远程调用是基于Netty发送Http请求实现,核心代码在NettyRoutingFilter.filter()
中,其中核心代码为send()方法
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
String scheme = requestUrl.getScheme();
if (!ServerWebExchangeUtils.isAlreadyRouted(exchange) && ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme))) {
ServerWebExchangeUtils.setAlreadyRouted(exchange);
ServerHttpRequest request = exchange.getRequest();
HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
String url = requestUrl.toASCIIString();
HttpHeaders filtered = HttpHeadersFilter.filterRequest(this.getHeadersFilters(), exchange);
DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
filtered.forEach(httpHeaders::set);
boolean preserveHost = (Boolean)exchange.getAttributeOrDefault(ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE, false);
Route route = (Route)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
Flux<HttpClientResponse> responseFlux = ((HttpClient.RequestSender)this.getHttpClient(route, exchange).headers((headers) -> {
headers.add(httpHeaders);
headers.remove("Host");
if (preserveHost) {
String host = request.getHeaders().getFirst("Host");
headers.add("Host", host);
}
}).request(method).uri(url)).send((req, nettyOutbound) -> {
if (log.isTraceEnabled()) {
nettyOutbound.withConnection((connection) -> {
log.trace("outbound route: " + connection.channel().id().asShortText() + ", inbound: " + exchange.getLogPrefix());
});
}
return nettyOutbound.send(request.getBody().map(this::getByteBuf));
}).responseConnection((res, connection) -> {
exchange.getAttributes().put(ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR, res);
exchange.getAttributes().put(ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR, connection);
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = new HttpHeaders();
res.responseHeaders().forEach((entry) -> {
headers.add((String)entry.getKey(), (String)entry.getValue());
});
String contentTypeValue = headers.getFirst("Content-Type");
if (StringUtils.hasLength(contentTypeValue)) {
exchange.getAttributes().put("original_response_content_type", contentTypeValue);
}
this.setResponseStatus(res, response);
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(this.getHeadersFilters(), headers, exchange, Type.RESPONSE);
if (!filteredResponseHeaders.containsKey("Transfer-Encoding") && filteredResponseHeaders.containsKey("Content-Length")) {
response.getHeaders().remove("Transfer-Encoding");
}
exchange.getAttributes().put(ServerWebExchangeUtils.CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet());
response.getHeaders().addAll(filteredResponseHeaders);
return Mono.just(res);
});
Duration responseTimeout = this.getResponseTimeout(route);
if (responseTimeout != null) {
responseFlux = responseFlux.timeout(responseTimeout, Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout))).onErrorMap(TimeoutException.class, (th) -> {
return new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th);
});
}
return responseFlux.then(chain.filter(exchange));
} else {
return chain.filter(exchange);
}
}
上面send方法最终会调用ChannelOperations#send()
方法,而该方法其实是基于了Netty实现数据发送。
至此,一次Spring Cloud Gateway
请求转发源码分析结束。