GateWay作为Spring Cloud的全新项目,目的是取代旧版本中的Netflix Zuul,从而实现更高的性能,本系列将会以下三个部分:
- 整体流程
- 全局CORS配置
- 动态路由刷新
Gateway接收到来自客户端的请求之后,请求首先进入HttpWebHandlerAdapter 类:
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
if (this.forwardedHeaderTransformer != null) {
request = this.forwardedHeaderTransformer.apply(request);
}
//过滤器中使用的ServerWebExchange就是在这里创建的DefaultServerWebExchange
ServerWebExchange exchange = createExchange(request, response);
LogFormatUtils.traceDebug(logger, traceOn ->
exchange.getLogPrefix() + formatRequest(exchange.getRequest()) +
(traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : ""));
//getDelegate()返回的是ExceptionHandlingWebHandler
return getDelegate().handle(exchange)
.doOnSuccess(aVoid -> logResponse(exchange))
.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
.then(Mono.defer(response::setComplete));
}
ExceptionHandlingWebHandler类的handle(exchange)方法
调用其父类WebHandlerDecorator的handle(exchange)方法,
最终会调用FilteringWebHandler的hanlde(exchange)方法。
//ExceptionHandlingWebHandler类
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
Mono<Void> completion;
try {
//调用父类WebHandlerDecorator的handle方法
completion = super.handle(exchange);
}
catch (Throwable ex) {
completion = Mono.error(ex);
}
for (WebExceptionHandler handler : this.exceptionHandlers) {
completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));
}
return completion;
}
//WebHandlerDecorator类
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
//调用FilteringWebHandler.handle(exchange)方法
return this.delegate.handle(exchange);
}
//FilteringWebHandler类
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
return this.chain.filter(exchange);
}
然后进入DefaultWebFilterChain的filter(exchange)方法
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
/*1.先执行所有的 WebFilter
特别注意:WebFilter不是Gateway特有的网关全局GlobalFilter和自定义的AbstractGatewayFilterFactory
2.然后执行DispatcherHandler的handle方法
DispatcherHandler可类比Spring MVC的DispatcherServlet
DispatcherHandler的三个属性就很清楚的表明了它的功能
通过handlerMappings确定具体处理网关请求的handler
通过handlerAdapters确定具体处理handler的handlerAdapter并处理handler
最终由resultHandler生成返回结果
private List<HandlerMapping> handlerMappings;
private List<HandlerAdapter> handlerAdapters;
private List<HandlerResultHandler> resultHandlers;
*/
return Mono.defer(() ->
this.currentFilter != null && this.next != null ?
this.currentFilter.filter(exchange, this.next) :
this.handler.handle(exchange));
}
核心类DispatcherHandler:
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
/*需要注意的就是Flux的next()方法
如果客户端请求的URL与Gateway中通过@RestController自定义的API路径
和网关路由定义的转发路径都匹配时,handlerMappings会匹配多个handler
通过next()方法只取出第一个匹配的handler执行下面的调用方法*/
return Flux.fromIterable(this.handlerMappings)
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
.switchIfEmpty(createNotFoundError())
.flatMap(handler -> invokeHandler(exchange, handler))
.flatMap(result -> handleResult(exchange, result));
}
mapping.getHandler(exchange)方法会执行AbstractHandlerMapping的getHandler(exchange)方法,我们在配置文件中指定的全局跨域规则就是在这个方法里生效的!
@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {
/*getHandlerInternal方法是一个抽象方法
由AbstractHandlerMapping的子类实现该方法
*/
return getHandlerInternal(exchange).map(handler -> {
if (logger.isDebugEnabled()) {
logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
}
/* REQUEST_HANDLED_HANDLER是一个函数式接口 exhange->Mono.empty()
如果客户端请求是跨域请求,就会去读区配置文件
判断当前请求是否允许跨域
*/
if (CorsUtils.isCorsRequest(exchange.getRequest())) {
CorsConfiguration configA = this.corsConfigurationSource.getCorsConfiguration(exchange);
CorsConfiguration configB = getCorsConfiguration(handler, exchange);
CorsConfiguration config = (configA != null ? configA.combine(configB) : configB);
/*
CORS的核心 使用的是Spring5特有的PathPattern
这里先给出跨域配置样例,后续会专门分析这个方法
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/users/*/message_roaming/*].allowedOrigins=*
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/users/*/message_roaming/*].allowedHeaders=*
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/users/*/message_roaming/*].allowedMethods=*
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/users/*/message_roaming/*].allowCredentials=true
spring.cloud.gateway.globalcors.corsConfigurations.[/*/*/users/*/message_roaming/*].maxAge=1728000
需要特别注意的就是,如果客户端请求是跨域请求
如果存在配置属性与之匹配,就会放行
如果不存在,则会拒绝该请求,设置ResponseHeader,返回REQUEST_HANDLED_HANDLER,
*/
if (!getCorsProcessor().process(config, exchange) ||
CorsUtils.isPreFlightRequest(exchange.getRequest())) {
return REQUEST_HANDLED_HANDLER;
}
}
return handler;
});
}
AbstractHandlerMapping抽象类有很多实现类,只关注用来处理路由转发的RoutePredicateHandlerMapping类。
RoutePredicateHandlerMapping的getHandlerInternal方法返回FilteringWebHandler类,然后就会返回执行DispatcherHandler的handle方法执行handler -> invokeHandler(exchange, handler)方法
//
private final FilteringWebHandler webHandler;
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
/*引入spring-boot-actuator组件后,为了安全性考虑,
可以通过management.server.port指定服务监控端口,
该端口下的请求URL是不需要经过Predicates校验的
*/
// don't handle requests on management port if set and different than server port
if (this.managementPortType == DIFFERENT && this.managementPort != null
&& exchange.getRequest().getURI().getPort() == this.managementPort) {
return Mono.empty();
}
exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());
/*lookupRoute方法将根据配置文件中指定的predicates断言器和请求路径做匹配
如果匹配成功就会返回FilteringWebHandler*/
return lookupRoute(exchange)
// .log("route-predicate-handler-mapping", Level.FINER) //name this
.flatMap((Function<Route, Mono<?>>) r -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isDebugEnabled()) {
logger.debug(
"Mapping [" + getExchangeDesc(exchange) + "] to " + r);
}
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
//FilteringWebHandler
return Mono.just(webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isTraceEnabled()) {
logger.trace("No RouteDefinition found for ["
+ getExchangeDesc(exchange) + "]");
}
})));
}
invokeHandler(exchange, handler)方法如下:
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
if (this.handlerAdapters != null) {
/*找到支持FilteringWebHandler的HandlerAdapter
然后调用FilteringWebHandler的handle方法*/
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
/*重点关注SimpleHandlerAdapter
SimpleHandlerAdapter用于执行gateway过滤器链*/
if (handlerAdapter.supports(handler)) {
return handlerAdapter.handle(exchange, handler);
}
}
}
return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}
SimpleHandlerAdapter执行的是FilteringWebHandler类的handle方法
public class SimpleHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return WebHandler.class.isAssignableFrom(handler.getClass());
}
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
//FilteringWebHandler类
WebHandler webHandler = (WebHandler) handler;
Mono<Void> mono = webHandler.handle(exchange);
return mono.then(Mono.empty());
}
}
FilteringWebHandler类的handle方法如下:
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
/*Gateway自定义多种过滤器,
同时我们可以通过实现AbstractGatewayFilterFactory类自定义局部过滤器*/
List<GatewayFilter> gatewayFilters = route.getFilters();
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
combined.addAll(gatewayFilters);
//对全局过滤器和配置文件指定的路由局部过滤器统一排序
// TODO: needed or cached?
AnnotationAwareOrderComparator.sort(combined);
if (logger.isDebugEnabled()) {
logger.debug("Sorted gatewayFilterFactories: " + combined);
}
//生成Gateway过滤器链,然后对客户端请求进行处理
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
GatewayFilterChain过滤器链中真正执行后端服务调用的Filter是NettyRoutingFilter,有兴趣的同学可以阅读以下。
由于SimpleHandlerAdapter的handler方法返回的是mono.then(Mono.empty()),暂时没有发现后续的handleResult方法被调用。
此过程大致是Gateway路由转发的整体过程,Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty,在此记录下网关调优相关的Netty的两个核心配置参数:
public final class ReactorNetty {
// System properties names
/**
* Specifies whether the channel ID will be prepended to the log message when possible.
* By default it will be prepended.
*/
static final boolean LOG_CHANNEL_INFO =
Boolean.parseBoolean(System.getProperty("reactor.netty.logChannelInfo", "true"));
/**
* Default worker thread count, fallback to available processor
* (but with a minimum value of 4)
*/
public static final String IO_WORKER_COUNT = "reactor.netty.ioWorkerCount";
/**
* Default selector thread count, fallback to -1 (no selector thread)
*/
public static final String IO_SELECT_COUNT = "reactor.netty.ioSelectCount";
}
(*^__^*)(*^__^*)(*^__^*)