服务网关GateWay原理

文章目录

上一篇博文我们讲解了GateWay的基础用法 服务网关,接下来我们就一起基于源码梳理一下GateWay的核心流程。本篇博文基于 Spring cloud gateWay 2.2.5RELEASE版本进行学习

先贴上官网中gateway整合请求完成流程:

自动装配核心类

GateWay是基于SpringBoot自动装配在我们的项目中启作用的。有关自动装配的文章参考:springBoot的自动装配。所以基于我们引入的依赖spring-cloud-starter-gateway来作为入口。入口没有配置自动装配类的文件spring.factories,所以继续往下探究。

spring-cloud-starter-gateway引入了

  • spring-cloud-gateway-core 网关核心
  • spring-boot-starter-webflux 响应式编程依赖
  • spring-cloud-starter-loadbalancer 从注册中心获取服务的负载

webflux、loadbalance都是辅助GateWay的组件,所以继续往spring-cloud-gateway-core里查找,找到对应的spring.factories。

自动装配类 作用
GatewayClassPathWarningAutoConfiguration webFlux没有引入的警告
GatewayAutoConfiguration 网关自动装配核心
GatewayHystrixCircuitBreakerAutoConfiguration 熔断器自动装配
GatewayResilience4JCircuitBreakerAutoConfiguration 在3.0.0.RELEASE中,已经被GatewayReactiveLoadBalancerClientAutoConfiguration替代 GatewayResilience4JCircuitBreakerAutoConfiguration实现ReactiveLoadBalancerClientFilter
GatewayLoadBalancerClientAutoConfiguration 引入ribbon时的负载均衡自动装配
GatewayNoLoadBalancerClientAutoConfiguration 没有引入ribbon时的负载均衡自动装配
GatewayMetricsAutoConfiguration 整合监控相关,提供监控指标。与actuators配合
GatewayRedisAutoConfiguration 与redis结合,可使用到限流等方面
GatewayDiscoveryClientAutoConfiguration 与服务发现组件进行结合起作用
SimpleUrlHandlerMappingGlobalCorsAutoConfiguration 支持cors-configurations配置,对CORS请求预检
GatewayReactiveLoadBalancerClientAutoConfiguration ReactiveLoadBalancerClientFilter实例化 LoadBalancerClientFilter与ReactiveLoadBalancerClientFilter只会实例化一个

GatewayAutoConfiguration

​ gateway的自动装配类中比较重要的是 GatewayAutoConfiguration, 它负责很多bean的初始化,它的类声明如下:

java 复制代码
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnProperty(
    name = {"spring.cloud.gateway.enabled"},
    matchIfMissing = true
)
@EnableConfigurationProperties
@AutoConfigureBefore({HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class})
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass({DispatcherHandler.class})
public class GatewayAutoConfiguration {
  //.....
}
  • 配置类:@Configuration声明GatewayAutoConfiguration是一个配置类,proxyBeanMethods声明配置类不会被代理提高性能。
  • 声明在配置spring.cloud.gateway.enabled值为true时加载GatewayAutoConfiguration配置,默认值是true。
  • 在HttpHandlerAutoConfiguration 和 WebFluxAutoConfiguration之前加载
  • 在GatewayLoadBalancerClientAutoConfiguration 和 GatewayClassPathWarningAutoConfiguration之后加载。
  • 注解@ConditionalOnClass作用只有DispatcherHandler类对应的bean实例存在才启动该配置

重要的bean实例

自动配置类GatewayAutoConfiguration在内部初始化了很多bean,列举几个重要的如下:

bean对象 作用
PropertiesRouteDefinitionLocator 用于从配置文件(yml/properties)中读取路由配置信息
RouteDefinitionLocator 把 RouteDefinition 转化为 Route
RoutePredicateHandlerMapping 类似于 mvc 的HandlerMapping,不过这里是 Gateway实现的。用于匹配对应的请求route
GatewayProperties 网关的配置信息封装在 GatewayProperties 对象中
AfterRoutePredicateFactory 各种路由断言工厂,正是这些断言工厂在启动时已经生成对应的bean
RetryGatewayFilterFactory 各种 Gateway 过滤器,正是这些过滤器在启动时已经生成对应的bean
GlobalFilter 全局过滤器
DispatcherHandle GateWay服务请求分发的入口类(该类不是GatewayAutoConfiguration实现化得到)
CachingRouteLocator 基于缓存的路由定位器

DispatcherHandler

​ 该类实现了WebHandler 说明其是webFulx响应编程的请求分发的入口。

​ 实现了ApplicationContextAware,说明在spring容器启动时侯会通过setApplicationContext初始化器内部依赖。

java 复制代码
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
   //构造器
   public DispatcherHandler(ApplicationContext applicationContext) {
        this.initStrategies(applicationContext);
   }
   //容器启动后调用
   public void setApplicationContext(ApplicationContext applicationContext) {
        this.initStrategies(applicationContext);
   }
  
   //初始化该类的handlerMappings、handlerAdapters、resultHandlers组件
   protected void initStrategies(ApplicationContext context) {
        //获取所有符合条件的HandlerMapping
        Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        ArrayList<HandlerMapping> mappings = new ArrayList(mappingBeans.values());
        AnnotationAwareOrderComparator.sort(mappings);
        this.handlerMappings = Collections.unmodifiableList(mappings);
        //获取所有符合条件的HandlerAdapter
        Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        this.handlerAdapters = new ArrayList(adapterBeans.values());
        AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        //获取所有符合条件的HandlerResultHandler
        Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false);
        this.resultHandlers = new ArrayList(beans.values());
        AnnotationAwareOrderComparator.sort(this.resultHandlers);
    }
  
   //省略部分代码...
}

GateWay采用的是webFlux的响应式编程,其整个流程与spring mvc 类似。

框架 gateWay mvc
请求分发 DispatcherHandler DispatcherServlet
请求映射 HandlerMapping HandlerMapping
请求适配 HandlerAdapter HanderAdapter
请求处理 HandlerResultHandler Hander
  • DispatcherHandler:请求处理的调度中心。
  • HandlerMapping:将请求映射到处理程序handler对象。
  • HandlerAdapter:用来适配处理程序,也用来将DispatcherHandler与相应的真正对请求进行处理的handler解耦。
  • HandlerResultHandler:用来处理相应的处理程序的返回结果的程序。

HandlerMapping

创建DispatcherHandler实例化了6个HandlerMapping

HandlerMapping 请求映射 作用
WebFluxEndpointHandlerMapping 负责将HTTP请求映射到相应的端点处理器(webFlux)
ControllerEndpointHandlerMapping 负责将对/actuator端点的请求映射到相应的端点处理器。
RouterFunctionMapping 负责将接收到的请求与路由规则进行匹配
RequestMappingHandlerMapping 负责用于复杂的路由配置
RoutePredicateHandlerMapping 负责为Gateway的路由断言机制定制的HandlerMapping
SimpleUrlHandlerMapping 用于简单的url匹配

HandlerAdapter

创建DispatcherHandler实例化了3个HandlerAdapter

HandlerAdapter请求适配 作用
RequestMappingHandlerAdapter 负责将HTTP请求映射到相应的端点处理器(webFlux)
HandlerFunctionAdapter 对接口的路由请求进行处理
SimpleHandlerAdapter 通过适配器获取并执行全部过滤器。

HandlerResultHandler

创建DispatcherHandler实例化了4个HandlerResultHandler

HandlerResultHandler请求处理 作用
ResponseBodyResultHandler 处理使用@ResponseBody修饰的http请求的处理器
ResponseEntityResultHandler 处理的http请求的处理器
ServerResponseResultHandler 用于处理来自网关路由的响应结果的处理器
ViewResolutionResultHandler 它负责处理解析视图的结果。在Spring Cloud Gateway中,视图通常指的是我们想要转发请求到的目的地。

请求处理阶段

网关入口是ReactorHttpHandlerAdapter#apply方法,当HTTP请求进入该方法后会去获取请求的request和response,构建当次请求的上下文供后续filter使用。

apply方法
java 复制代码
public class ReactorHttpHandlerAdapter implements BiFunction<HttpServerRequest, HttpServerResponse, Mono<Void>>{
	@Override
	public Mono<Void> apply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) {
		NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(reactorResponse.alloc());
		try {
		  //获取请求的Request,构建ReactorServerHttpRequest
			ReactorServerHttpRequest request = new ReactorServerHttpRequest(reactorRequest, bufferFactory);
			//构建ServerHttpResponse
			ServerHttpResponse response = new ReactorServerHttpResponse(reactorResponse, bufferFactory);
      //Heade 请求 构建Header类型的response
			if (request.getMethod() == HttpMethod.HEAD) {
				response = new HttpHeadResponseDecorator(response);
			}
			//交给HttpWebHandlerAdapter构建上下文ServerWebExchange
			return this.httpHandler.handle(request, response)
          //失败
					.doOnError(ex -> logger.trace(request.getLogPrefix() + "Failed to complete: " + ex.getMessage()))
          //成功
					.doOnSuccess(aVoid -> logger.trace(request.getLogPrefix() + "Handling completed"));
		}
		catch (URISyntaxException ex) {
			//异常处理并返回
		}
	}
  //省略部分代码
}

this.httpHandler.handle 最终是调用org.springframework.web.server.adapter.HttpWebHandlerAdapter#handle进行处理。

httpHandler#handle方法
java 复制代码
	@Override
	public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
    //forwardedHeaderTransformer不为空,需要删除一些不需要往下传递的Headers
		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 
    //去处理ServerWebExchange网关的上下文对象
		return getDelegate().handle(exchange)
				.doOnSuccess(aVoid -> logResponse(exchange))
				.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
				.then(Mono.defer(response::setComplete));
	}

构建完request和response后,交给HttpWebHandlerAdapter构建上下文ServerWebExchange(实现类:DefaultServerWebExchange)。

并交由WebHandler#handle处理网关上下文。

WebHandler#handle方法

这里有一个区分HttpHanler接口是WebFulx响应式编程的接口规范。webHandler是Servlet的接口规范。getDelegate方法获取到的WebHandler是一个装饰类如图:

链路如下:

ExceptionHandlingWebHandler

---> FilteringWebHandler

​ ---> DispatcherHandler

  • ExceptionHandlingWebHandler 对WebHandler进行异常处理的包装

  • FilteringWebHandler 注意该类为org.springframework.web.server.handler.FilteringWebHandler而非org.springframework.cloud.gateway.handler.FilteringWebHandler

前者是web服务提供的filter处理器,后者是gateway提供的网关filter处理器。

  • DispatcherHandler 我们熟悉的网关请求处理入口。

ExceptionHandlingWebHandler#hander

java 复制代码
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
  Mono<Void> completion;
  //交给其父类去处理 WebHandlerDecorator的handle方法
  try {
    completion = super.handle(exchange);
  }
  catch (Throwable ex) {
    completion = Mono.error(ex);
  }
  //使用该类的异常处理类进行异常处理(ExceptionHandlingWebHandler主要作用)
  for (WebExceptionHandler handler : this.exceptionHandlers) {
    completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));
  }
  return completion;
}

将真实的请求交给委托父类处理,只对请求完成后的异常情况进行处理。

WebHandlerDecorator的handle

该方法调用的org.springframework.web.server.handler.FilteringWebHandler的handle

java 复制代码
public Mono<Void> handle(ServerWebExchange exchange) {
    return this.chain.filter(exchange);
}

DefaultWebFilterChain#filter

//该chain包含过滤器列表和一个Handler

  • allFilters 所有需要执行的过滤器组件(webFlux的过滤器)
  • DispatchHanlder 上文说到的网关请求的转发的入口。
java 复制代码
//chain为DefaultWebFilterChain
public Mono<Void> filter(ServerWebExchange exchange) {
  //执行所有的过滤器,过滤器执行完毕后将请求交给DispatchHanlderHandler进行处理
  return Mono.defer(() ->
                    this.currentFilter != null && this.chain != null ?
                    invokeFilter(this.currentFilter, this.chain, exchange) :
                    this.handler.handle(exchange));
}
DispatchHanlder#handle方法
java 复制代码
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
  //如果获取不到网关请求映射则返回未发现异常
  if (this.handlerMappings == null) {
    return createNotFoundError();
  }
  //响应式处理 三部完成
  return Flux.fromIterable(this.handlerMappings)
    //第一步 根据网关上下文获取对应的请求映射HandlerMapping
    .concatMap(mapping -> mapping.getHandler(exchange))
    .next()
    .switchIfEmpty(createNotFoundError())
    //第二步 执行请求处理
    .flatMap(handler -> invokeHandler(exchange, handler))
    //第三步 处理返回结果
    .flatMap(result -> handleResult(exchange, result));
}

mapping.getHandler

java 复制代码
@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {
    //getHandlerInternal 根据路由规则获取对应的Handler
    return getHandlerInternal(exchange).map(handler -> {
       if (logger.isDebugEnabled()) {
          logger.debug(exchange.getLogPrefix() + "Mapped to " + 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;
          }
       }
       return handler;
    });
}

该方法主要做两件事

  1. 根据路由规则获取对应的Handler
  2. 如果请求为跨域,则为该Handler添加跨域相关配置。

在上面我们分析创建DispatcherHandler实例化了6个HandlerMapping。其中跟网关有关的映射为RoutePredicateHandlerMapping

第一步 getHandler获取请求映射

RoutePredicateHandlerMapping#getHandlerInternal实现类获取路由信息

java 复制代码
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());
        //根据路由上下文 查询对应的Route信息
        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);
            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) + "]");
            }

        })));
    }
}

上面代码很多其实我们只需要关注lookupRoute 故名思义:根据路由上下文获得对应的路由(Route)信息。继续看lookupRoute。

lookupRoute方法

java 复制代码
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;
    });
}

根据路由条件获取对应的路由信息,其中所有配置的路由信息是存放在this.routeLocator(RouteLocator)中。RouteLocator在自动化装配类GatewayAutoConfiguration中被初始化。RouteLocator有如下三种实现。

  • RouteDefinitionRouteLocator 基于路由定义的定位器。
  • CachingRouteLocator 基于缓存的路由定位器。
  • CompositeRouteLocator 基于组合方式的路由定位器。
第二步 invokeHandler 请求适配

​ 上述getHandler 最终返回的是org.springframework.cloud.gateway.handler.FilteringWebHandler实例。接着通过适配器HandlerAdapters去处理FilteringWebHandler实例。

DispatcherHandler#invokeHandler

java 复制代码
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
    if (this.handlerAdapters != null) {
       //获取DispatcherHandler中的符合条件的HandlerAdapter进行请求适配
       //support 为判断方法 因为网关默认是WebHandler所有跟网关有关的适配器为SimpleHandlerAdapter
       for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
          if (handlerAdapter.supports(handler)) {
             return handlerAdapter.handle(exchange, handler);
          }
       }
    }
    return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}

同理,在上面我们分析创建DispatcherHandler实例化了3个HandlerAdapters 因为网关默认是WebHandler所有跟网关有关的适配器为SimpleHandlerAdapter

SimpleHandlerAdapter#handle

java 复制代码
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
    WebHandler webHandler = (WebHandler) handler;
    Mono<Void> mono = webHandler.handle(exchange);
    return mono.then(Mono.empty());
}

直接将handler转换为WebHandler调用其handle,最终就是调用FilteringWebHandler#handle方法执行请求

FilteringWebHandler#handle

java 复制代码
public Mono<Void> handle(ServerWebExchange exchange) {
    //从ServerWebExchange网关上下文中获取匹配到的路由信息,在上面匹配成功后会将路由信息存放在ServerWebExchange网关上下文属性中
    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);
    }
    //创建DefaultGatewayFilterChain 执行过滤器链
    return (new DefaultGatewayFilterChain(combined)).filter(exchange);
}

上面就是适配器的主要流程,主要分五步

  1. 从ServerWebExchange网关上下文中获取匹配到的路由信息
  2. 获取路由信息中配置的过滤器局部过滤器以及全局过滤器,合并到一起并排序
  3. 创建责任链DefaultGatewayFilterChain执行所有的过滤器

所有按照优先级排序后的过滤器如下:

比较重要的过滤器

过滤器 作用
RouteToRequestUrlFilter 将域名和端口转换成调用的服务,并且判断了是否需要进行负载均衡
LoadBalancerClientFilter 根据 lb://user-service 从注册中心获取到一个服务实例的ip和端口
NettyRoutingFilter 发送真实的业务请求
WebsocketRoutingFilter 可以处理"ws://","wss://"等 websocket协议。

其中有一个Filter即为请求转发到具体的服务节点的过滤器。从而执行真正的业务请求。笔者经过实验得到为NettyRoutingFilter来将请求调用真实业务请求。

java 复制代码
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    //获取真实的业务请求url
    URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
    String scheme = requestUrl.getScheme();
    //首次请求 且为Http或者Https则构建参数执行请求
    if (!ServerWebExchangeUtils.isAlreadyRouted(exchange) && ("http".equals(scheme) || "https".equals(scheme))) {       
        //标记完成(保证一次请求执行一次)
        ServerWebExchangeUtils.setAlreadyRouted(exchange);
        //获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        //method
        HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
        //url
        String url = requestUrl.toASCIIString();
        //请求头header
        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);
        //webFlux执行请求
        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().putAll(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);
    }
}
第三步 handleResult请求处理

其实到上面的过滤器链执行完毕后整个业务请求也执行完毕,如果需要在对业务请求进行相关的特殊处理则需要就到了最后一步进行请求处理。

笔者参考了公司的项目以及网上的相关实例,都没有找到使用HandlerResultHandler进行处理的场景,即使有想过返回值的处理情况,大部分的解决方案是使用自定义过滤器,不会使用handleResult

鉴于时机场景没有使用,这里只对该流程进行简单分析,在上面我们分析创建DispatcherHandler实例化了4个HandlerResultHandler。

java 复制代码
//获取符合了条件的HandlerResultHandler
private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
    if (this.resultHandlers != null) {
       for (HandlerResultHandler resultHandler : this.resultHandlers) {
          if (resultHandler.supports(handlerResult)) {
             return resultHandler;
          }
       }
    }
    throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
}
//调用HandlerResultHandler对业务逻辑进行处理。
Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result);
总结
  • Gateway Client向 Spring Cloud Gateway 发送请求。

  • 请求首先会被ReactorHttpHandlerAdapter接收,构造Resquest和Response。

  • 然后会被HttpWebHandlerAdapter 进行提取组装成网关上下文ServerWebExchange。

  • 然后网关的上下文会传递到DispatcherHandler ,它负责将请求分发给 RoutePredicateHandlerMapping。

  • RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用。

  • 如果过断言成功,由FilteringWebHandler 创建过滤器链并调用。

  • 通过特定于请求的 Fliter 链运行请求,Filter 被虚线分隔的原因是Filter可以在发送代理请求之前(pre)和之后(post)运行逻辑。

  • 执行所有pre过滤器逻辑。然后进行代理请求。发出代理请求后,将运行"post"过滤器逻辑。

  • 处理完毕之后将 Response 返回到 Gateway 客户端。

相关推荐
菜菜-plus2 小时前
分布式,微服务,SpringCloudAlibaba,nacos,gateway,openFeign
java·分布式·微服务·nacos·gateway·springcloud·openfeign
七月在野,八月在宇,九月在户2 小时前
前端--> nginx-->gateway产生的跨域问题分析
前端·nginx·gateway
Denny辉2 天前
Flink使用SQL Gateway提交SQL Job到远程集群
sql·flink·gateway
菜菜-plus2 天前
Nginx、Gateway的区别
运维·nginx·gateway
cyt涛4 天前
SpringCloudGateway — 网关登录校验
运维·网关·gateway·登录·过滤器·校验·threadlocal
牛角上的男孩6 天前
Istio Gateway发布服务
云原生·gateway·istio
柳叶寒8 天前
医院信息化与智能化系统(17)
java·nacos·gateway·全栈·项目
cyt涛8 天前
SpringCloudGateway — 网关路由
java·开发语言·网关·gateway·路由·断言·转发
代码魔法师Sunny10 天前
502 Bad Gateway 错误详解:从表现推测原因,逐步排查直至解决
gateway
RedCong11 天前
通过route访问Openshift上的HTTP request报错504 Gateway Time-out【已解决】
http·gateway·openshift