两年前的笔记了,以后有时间画个图吧
Gateway 的路由规则是由一组 Route 、Predicate 和 Filter 构成,三者协作完成网关的请求转发; RouteDefinitionLocator 负责加载 RouteDefinition ,再由 RouteDefinitionRouteLocator 转换为 Route ,最后由 RoutePredicateHandlerMapping 选择出匹配的 Route ,执行请求转发的动作。
Gateway底层并没有采用SpringMVC而是使用的webFlux的响应式编程,其整个流程和SpringMVC类似,具体的组件和方法对应如下:
- DispatcherHandler -> DispatcherServlet
- handle() -> doDispatch()
- HandlerMapping -> HandlerMapping
- HandlerAdapter -> HandlerAdapter
- WebHandler -> HandlerMethod
通过如上对比,我们可以知道肯定所有的请求都会经过DispatcherHandler#handle()
handle()
@Override public Mono<Void> handle(ServerWebExchange exchange) { if (this.handlerMappings == null) { return createNotFoundError(); } // 1. 遍历每一个 handlerMapping (从容器中拿到的,核心类 = RoutePredicateHandlerMapping) return Flux.fromIterable(this.handlerMappings) // 2. 调用每一个handlerMapping的getHandler()方法 .concatMap(mapping -> mapping.getHandler(exchange)) .next() .switchIfEmpty(createNotFoundError()) // 3. 遍历每一个HandlerAdapter,调用其support方法判断是否支持处理getHandler()返回的handler // 找到对应的HandlerAdapter之后调用其handle()方法进行处理 .flatMap(handler -> invokeHandler(exchange, handler)) // 4. 处理返回值(由于Gateway仅仅只是负责请求的分发,并不会对服务的响应进行额外处理,因此这一步会直接返回) .flatMap(result -> handleResult(exchange, result)); }
getHandler()
```java // AbstractHandlerMapping // 和SpringMVC一样,也是先进入一个抽象类做一些公共逻辑,再调用子类重写的getHandlerInternal()方法(模板设计模式) @Override public Mono
return getHandlerInternal(exchange).map(handler -> {
ServerHttpRequest request = exchange.getRequest(); // 跨域的相关处理 if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null); CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange); config = (config != null ? config.combine(handlerConfig) : handlerConfig); if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) { return REQUEST_HANDLED_HANDLER; } } // 返回handler return handler; }); }
// RoutePredicateHandlerMapping.getHandlerInternal() // HandlerMapping有多个实现类,这里会进入GatewayAutoConfiguration中配置的RoutePredicateHandlerMapping @Override protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
// 1. 查找到当前请求路径匹配的路由
return lookupRoute(exchange)
.flatMap((Function<Route, Mono<?>>) r -> {
// 2. 将当前路由与exchange上下文绑定:gatewayRoute :route
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
// 3. 返回一个webHandler(类似于HandlerMethod)
return Mono.just(webHandler);
}); }
protected Mono
// 1. 调用routeLocator.getRoutes()方法获取到所有的Route(1)
// routeLocator = CachingRouteLocator,里面保存了所有的RouteLocator
return this.routeLocator.getRoutes()
// 2. 遍历每一个Route,获取到内部的断言工厂并执行断言,判断该路由是否符合条件(2)
.concatMap(route -> Mono.just(route).filterWhen(r -> {
// 3. 执行当前Route中的断言工厂方法进行路由选择
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;
}); } ``` (1)先来看1,是如何获取到所有的路由的 ```java // RouteDefinitionRouteLocator @Override public Flux<Route> getRoutes() {
// 自动配置的routeDefinitionLocator = CompositeRouteDefinitionLocator,里面包含了所有的routeDefinitionLocator
// 我们可以有多种方式定义Route(例如yaml或者Java类)
// 1. 通过routeDefinitionLocator拿到RouteDefinition(下面会介绍yaml或者properties方式的RouteDefinition获取)
Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions()
// 2. 根据routeDefinition创建实际的Route对象(下面会讲根据RouteDefinition创建Route实例的逻辑)
.map(this::convertToRoute);
return routes.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
}); }
// 以yaml/properties配置为例,会进入到PropertiesRouteDefinitionLocator @Override public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
// @ConfigurationProperties(GatewayProperties.PREFIX) -> "spring.cloud.gateway"
// SpringBoot容器刷新的时候,ConfigurationPropertiesPostProcessor就会解析yaml中的路由配置并添加到GatewayProperties中(setRoutes)
private final GatewayProperties properties;
public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
this.properties = properties;
}
// 从GatewayProperties中拿到所有的路由
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.properties.getRoutes());
}
}
// 根据routeDefinition创建实际的Route对象
private Route convertToRoute(RouteDefinition routeDefinition) {
// 通过RouteDefinition配置的断言信息,拿到所有的断言工厂并封装(1.1)
AsyncPredicate
// 创建Route对象,里面就包含了断言工厂和GatewayFilter
return Route.async(routeDefinition).asyncPredicate(predicate)
.replaceFilters(gatewayFilters).build(); } ``` (1.1)先来看如何获取到断言工厂的 ```java private AsyncPredicate<ServerWebExchange> combinePredicates(
RouteDefinition routeDefinition) {
// 1. 拿到所有的断言定义信息
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
if (predicates == null || predicates.isEmpty()) {
return AsyncPredicate.from(exchange -> true);
}
// 2. 先单独拿到一个断言实例
AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
predicates.get(0));
// 3. 拼接多个断言工厂
for (PredicateDefinition andPredicate : predicates.subList(1,
predicates.size())) {
AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
andPredicate);
predicate = predicate.and(found);
}
return predicate; }
private AsyncPredicate
// 1. 通过断言定义信息拿到指定的断言工厂(predicates保存了容器中所有断言工厂,自动配置包创建的)
RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
return factory.applyAsync(config); } ``` (1.2)再来看看如何获取到过滤器对象的 ```java private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
List<GatewayFilter> filters = new ArrayList<>();
// 1. 先从gatewayProperties拿到指定的DefaultFilters
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
// 2. 再通过FileterName去gatewayFilterFactories中拿到具体的GatewayFilterFactory
filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
new ArrayList<>(this.gatewayProperties.getDefaultFilters())));
}
// 3. 再从gatewayProperties拿到指定的Filters
if (!routeDefinition.getFilters().isEmpty()) {
// 4. 再通过FileterName去gatewayFilterFactories中拿到具体的GatewayFilterFactory
filters.addAll(loadGatewayFilters(routeDefinition.getId(),
new ArrayList<>(routeDefinition.getFilters())));
}
AnnotationAwareOrderComparator.sort(filters);
return filters; } ```
(2)再来看2,是如何调用所有的断言工厂进行路由选择的
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
// 1. 调用routeLocator.getRoutes()方法获取到所有的Route(1)
return this.routeLocator.getRoutes()
// 2. 遍历每一个Route,获取到内部的断言工厂并执行断言,判断该路由是否符合条件(2)
.concatMap(route -> Mono.just(route).filterWhen(r -> {
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
// 3. 执行当前Route中的断言工厂方法进行路由选择,返回true表示过滤成功,最下面的map中就会返回这个route
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;
});
}
@Override
public Publisher<Boolean> apply(T t) {
return Mono.just(delegate.test(t));
}
// 再往后就会进入实际判断逻辑了,找到匹配当前请求的路由,这里以Path断言为例
// PathRoutePredicateFactory#apply → GatewayPredicate
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
// 解析出当前的请求uri
PathContainer path = parsePath(exchange.getRequest().getURI().getRawPath());
// 从所有内置的路由规则中匹配是否有合适的路由
Optional<PathPattern> optionalPathPattern = pathPatterns.stream()
.filter(pattern -> pattern.matches(path)).findFirst();
// 匹配到了,解析uri上的参数,并放入请求上下文参数中,留给后续的过滤器使用
if (optionalPathPattern.isPresent()) {
PathPattern pathPattern = optionalPathPattern.get();
traceMatch("Pattern", pathPattern.getPatternString(), path, true);
PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
return true;
} else {
traceMatch("Pattern", config.getPatterns(), path, false);
return false;
}
}
此时,路由选择lookupRoute()方法结束,再回到之前的getHandlerInternal,此时会把路由设置到exchange上下文中,并直接返回一个webHandler
// RoutePredicateHandlerMapping.getHandlerInternal()
// HandlerMapping有多个实现类,这里会进入GatewayAutoConfiguration中配置的RoutePredicateHandlerMapping
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
// 给exchange(封装了Request和Response)中设置属性:gatewayHandlerMapper:RoutePredicateHandlerMapping
exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());
// 1. 查找到当前请求路径匹配的路由
return lookupRoute(exchange)
.flatMap((Function<Route, Mono<?>>) r -> {
// 移除exchange上下文中旧的属性:gatewayPredicateRouteAttr
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
// 2. 将满足断言条件的路由与exchange上下文绑定:gatewayRoute :route
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
// 3. 返回一个webHandler(类似于HandlerMethod),webHandler在自动配置类中创建的时候,构造方法依赖注入了GlobalFilter,同时其构造方法对GlobalFilter进行了适配,适配成GatewayFilter
return Mono.just(webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
})));
}
再往后就又回来DispatcherHandler.handle()方法了
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
return Flux.fromIterable(this.handlerMappings)
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
.switchIfEmpty(createNotFoundError())
// 开始调用invokeHandler(),这里的handler就是刚才返回的webHandler
.flatMap(handler -> invokeHandler(exchange, handler))
.flatMap(result -> handleResult(exchange, result));
}
进入invokeHandler()
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
// 可以看到,和SpringMVC选择handlerAdapter的逻辑一样,遍历每一个handlerAdapter调用其supports方法
if (this.handlerAdapters != null) {
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
if (handlerAdapter.supports(handler)) {
return handlerAdapter.handle(exchange, handler);
}
}
}
return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}
这里只有SimpleHandlerAdapter会匹配成功,因为我们handler的类型为webHandler(这是接口,实际类型为Gateway包下的FilteringWebHandler),匹配成功之后就会调用SimpleHandlerAdapter的handle方法
public class SimpleHandlerAdapter implements HandlerAdapter {
// 匹配到WebHandler
@Override
public boolean supports(Object handler) {
return WebHandler.class.isAssignableFrom(handler.getClass());
}
// 可以看到,handle方法实际上是调用的webHandler的handle方法,同时返回的是一个空
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
WebHandler webHandler = (WebHandler) handler;
Mono<Void> mono = webHandler.handle(exchange);
return mono.then(Mono.empty());
}
}
再来看webHandler的handle方法
// gateway包下的FilteringWebHandler
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
// 1. 首先拿到Route
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
// 2. 拿到Route的过滤器
List<GatewayFilter> gatewayFilters = route.getFilters();
// 3. 拿到全局过滤器并和Route的过滤器合并到一个集合中
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
combined.addAll(gatewayFilters);
// 4. 按照order排序
AnnotationAwareOrderComparator.sort(combined);
// 5. 封装filter为一个执行链并调用filter方法开始执行
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
上面有一个细节,我们的全局Filter明明是GlobalFilter,为啥能和Route级别的GatewayFilter放到一个集合呢?这里其实用到了适配器模式
public class FilteringWebHandler implements WebHandler {
// 保存的时候保存GatewayFilter
private final List<GatewayFilter> globalFilters;
// 创建的时候传入GlobalFilter
public FilteringWebHandler(List<GlobalFilter> globalFilters) {
// 适配GlobalFilter -> GatewayFilter
this.globalFilters = loadFilters(globalFilters);
}
// ...
}
// 进入loadFilters()
private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
return filters.stream().map(filter -> {
// 将每一个GlobalFilter封装到GatewayFilterAdapter(GatewayFilter的实现类)
GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
if (filter instanceof Ordered) {
int order = ((Ordered) filter).getOrder();
return new OrderedGatewayFilter(gatewayFilter, order);
}
return gatewayFilter;
}).collect(Collectors.toList());
}
// 之后调用filter方法实际上是调用的GlobalFilter.filter()方法
private static class GatewayFilterAdapter implements GatewayFilter {
private final GlobalFilter delegate;
GatewayFilterAdapter(GlobalFilter delegate) {
this.delegate = delegate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
}
Filter
RouteToRequestUrlFilter ```java @Override public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 从属性取出之前找到的路由信息Route Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); if (route == null) { return chain.filter(exchange); }
// 2. 拿到客户端原始请求URI(http://网关ip:port/path)
URI uri = exchange.getRequest().getURI();
boolean encoded = containsEncodedParts(uri);
// 3. 取出路由信息中的目标URI(lb://服务名)
URI routeUri = route.getUri();
// 4. Load balanced URIs should always have a host
// 4. route中的负载均uri必须要有host,也就是服务名
if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {
throw new IllegalStateException("Invalid host: " + routeUri.toString());
}
// 5. 将原有url与路由规则中的uri拼接为真正能被负载均衡解析的url
// 得到lb://服务名/path
URI mergedUrl = UriComponentsBuilder.fromUri(uri)
// .uri(routeUri)
.scheme(routeUri.getScheme()).host(routeUri.getHost())
.port(routeUri.getPort()).build(encoded).toUri();
// 6. 以gatewayRequestUrl为属性名,将拼接后的url放到exchange的属性中(lb://服务名/path)
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
// 继续往下
return chain.filter(exchange); } ```
LoadBalancerClientFilter(其自动配置会配置LoadBalancerClient -> RibbonLoadBalancerClient) ```java @Override @SuppressWarnings(“Duplicates”) public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 拿到上一个filter拼接后的url(lb://服务名/path) URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); // gatewayRequestUrl String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
// 2. 当前filter只处理负载均衡lb(所以lb的作用其实就这一步,如果没有lb不会走下面负载均衡)
if (url == null
|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
return chain.filter(exchange);
}
// 3. 将拼接后的 url = lb://服务名/path 放到gatewayOriginalRequestUrl属性中
addOriginalRequestUrl(exchange, url);
// loadBalancer.choose(((URI) exchange.getAttribute("GATEWAY_REQUEST_URL_ATTR")).getHost())
// 4. 拿到之前设置的 url 中的 host 也就是服务名,调用LoadBalancerClient的choose()方法获取一个服务节点
final ServiceInstance instance = choose(exchange);
// 5. 再拿到原始请求URI,待会就会替换掉这个里面的host
URI uri = exchange.getRequest().getURI();
// 判断http和https
String overrideScheme = instance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
// 6. 使用拿到服务实例instance的ip端口替换掉原始请求URI中的host
URI requestUrl = loadBalancer.reconstructURI(
new DelegatingServiceInstance(instance, overrideScheme), uri);
// 7. 将最终的服务调用URL设置到exchange的GATEWAY_REQUEST_URL_ATTR属性中(这里会覆盖掉之前拼接后的url)
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange); } ```
NettyRoutingFilter ```java @Override @SuppressWarnings(“Duplicates”) public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 拿到最终URL URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
// 不是http协议无法处理
String scheme = requestUrl.getScheme();
if (isAlreadyRouted(exchange)
|| (!"http".equals(scheme) && !"https".equals(scheme))) {
return chain.filter(exchange);
}
// 标记该请求已经被路由处理
setAlreadyRouted(exchange);
// 1. 拿到客户端的request的方法、URL
ServerHttpRequest request = exchange.getRequest();
final HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
final String url = requestUrl.toASCIIString();
// 2. 拿到客户端的reqeust中的请求头
HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);
final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
filtered.forEach(httpHeaders::set);
boolean preserveHost = exchange
.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange)
.headers(headers -> { // 3. 设置对路由转发的服务节点的请求头
headers.add(httpHeaders);
// Will either be set below, or later by Netty
headers.remove(HttpHeaders.HOST);
if (preserveHost) {
String host = request.getHeaders().getFirst(HttpHeaders.HOST);
headers.add(HttpHeaders.HOST, host);
}
// 4. 设置设置对路由转发的服务节点的请求方法、URL、请求体并发送请求
}).request(method).uri(url).send((req, nettyOutbound) -> {
return nettyOutbound.send(request.getBody().map(this::getByteBuf));
}).responseConnection((res, connection) -> {
// 5. 路由服务节点的响应结果放到exchange属性中
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);
// 6. 拿到对客户端的响应
ServerHttpResponse response = exchange.getResponse();
// 7. 拿到路由服务节点的响应头
HttpHeaders headers = new HttpHeaders();
res.responseHeaders().forEach(
entry -> headers.add(entry.getKey(), entry.getValue()));
// 8. 拿到路由服务节点的响应头中的content-type并设置到exchange的属性中
String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
if (StringUtils.hasLength(contentTypeValue)) {
exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR,
contentTypeValue);
}
// 9. 设置路由服务节点的 响应状态码和响应码 描述到客户端的响应中(200 ok)
setResponseStatus(res, response);
// 10. 拿到过滤之后的路由服务节点的 响应头
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
getHeadersFilters(), headers, exchange, Type.RESPONSE);
// 11. 将过滤之后的路由服务节点的响应头放到exchange的属性中
exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES,
filteredResponseHeaders.keySet());
// 12. 将过滤之后的路由服务节点的响应头设置到客户端的响应中
response.getHeaders().putAll(filteredResponseHeaders);
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)); } ``` 从此之后filter就该往回走了
NettyWriteResponseFilter ```java @Override public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 之前来的时候先走的这里 return chain.filter(exchange) // 2. filter回来之后再进行额外的处理 .doOnError(throwable -> cleanup(exchange)) .then(Mono.defer(() -> { // 3. 拿到一开始设置的针对服务节点的连接 Connection connection = exchange.getAttribute(CLIENT_RESPONSE_CONN_ATTR);
// 4. 拿到针对客户端的响应(此时里面 响应行 和 响应头 都已经设置好了)
ServerHttpResponse response = exchange.getResponse();
// 5. 设置响应体
final Flux<DataBuffer> body = connection
.inbound()
.receive()
.retain()
.map(byteBuf -> wrap(byteBuf, response));
MediaType contentType = null;
try {
contentType = response.getHeaders().getContentType();
}
catch (Exception e) {
if (log.isTraceEnabled()) {
log.trace("invalid media type", e);
}
}
// 6. 给客户端发送HTTP响应
return (isStreamingMediaType(contentType)
? response.writeAndFlushWith(body.map(Flux::just))
: response.writeWith(body));
})).doOnCancel(() -> cleanup(exchange));
// @formatter:on } ``` 至此,客户端就收到对应的响应了 注意:整个SimpleHandlerAdapter在调用玩webHandler.handle()方法之后,再返回的结果是一个空,因此DispatcherHandler的handleResult()方法会直接返回,因为本质上Gateway仅仅只是一个中间者,他只负责路由转发请求,不需要处理结果