服务网关GateWay原理

文章目录

      • 自动装配核心类
      • GatewayAutoConfiguration
      • DispatcherHandler
      • 请求处理阶段
          • apply方法
          • httpHandler#handle方法
          • WebHandler#handle方法
          • DispatchHanlder#handle方法
          • 第一步 getHandler获取请求映射
          • 第二步 invokeHandler 请求适配
          • 第三步 handleResult请求处理
          • 总结

上一篇博文我们讲解了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

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

GatewayAutoConfiguration

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

@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全局过滤器
DispatcherHandleGateWay服务请求分发的入口类(该类不是GatewayAutoConfiguration实现化得到)
CachingRouteLocator基于缓存的路由定位器

在这里插入图片描述

DispatcherHandler

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

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

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) {//获取所有符合条件的HandlerMappingMap<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);//获取所有符合条件的HandlerAdapterMap<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);this.handlerAdapters = new ArrayList(adapterBeans.values());AnnotationAwareOrderComparator.sort(this.handlerAdapters);//获取所有符合条件的HandlerResultHandlerMap<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false);this.resultHandlers = new ArrayList(beans.values());AnnotationAwareOrderComparator.sort(this.resultHandlers);}//省略部分代码...
}

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

框架gateWaymvc
请求分发DispatcherHandlerDispatcherServlet
请求映射HandlerMappingHandlerMapping
请求适配HandlerAdapterHanderAdapter
请求处理HandlerResultHandlerHander
  • 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方法
public class ReactorHttpHandlerAdapter implements BiFunction<HttpServerRequest, HttpServerResponse, Mono<Void>>{@Overridepublic Mono<Void> apply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) {NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(reactorResponse.alloc());try {//获取请求的Request,构建ReactorServerHttpRequestReactorServerHttpRequest request = new ReactorServerHttpRequest(reactorRequest, bufferFactory);//构建ServerHttpResponseServerHttpResponse response = new ReactorServerHttpResponse(reactorResponse, bufferFactory);//Heade 请求 构建Header类型的responseif (request.getMethod() == HttpMethod.HEAD) {response = new HttpHeadResponseDecorator(response);}//交给HttpWebHandlerAdapter构建上下文ServerWebExchangereturn 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方法
	@Overridepublic Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {//forwardedHeaderTransformer不为空,需要删除一些不需要往下传递的Headersif (this.forwardedHeaderTransformer != null) {request = this.forwardedHeaderTransformer.apply(request);}//构建ServerWebExchange网关的上下文对象 默认实例:DefaultServerWebExchangeServerWebExchange 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

@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

public Mono<Void> handle(ServerWebExchange exchange) {return this.chain.filter(exchange);
}

DefaultWebFilterChain#filter
在这里插入图片描述
//该chain包含过滤器列表和一个Handler

  • allFilters 所有需要执行的过滤器组件(webFlux的过滤器)
  • DispatchHanlder 上文说到的网关请求的转发的入口。
//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方法
@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

@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {//getHandlerInternal 根据路由规则获取对应的Handlerreturn 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实现类获取路由信息

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方法

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

private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {if (this.handlerAdapters != null) {//获取DispatcherHandler中的符合条件的HandlerAdapter进行请求适配//support 为判断方法 因为网关默认是WebHandler所有跟网关有关的适配器为SimpleHandlerAdapterfor (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

@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

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来将请求调用真实业务请求。

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//获取真实的业务请求urlURI 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();//methodHttpMethod method = HttpMethod.valueOf(request.getMethodValue());//urlString url = requestUrl.toASCIIString();//请求头headerHttpHeaders 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。

//获取符合了条件的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 客户端。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/6354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Gitea 上传用户签名

在 Gitea 的用户管理部分&#xff0c;有一个 SSH 和 GPG 的选项。 单击这个选项&#xff0c;可以在选项上添加 Key。 Key 的来源 如是 Windows 的用户&#xff0c;可以选择 Kleopatra 这个软件。 通过这个软件生成的 Key 的界面中有一个导出功能。 单击这个导出&#xff0c;…

OpenWRT有线桥接部署教程

前言 之前咱们讲到OpenWRT部署WAN实现PPPoE拨号上网和自动获取IP模式上网的办法&#xff1a; OpenWRT设置PPPoE拨号教程 OpenWRT设置自动获取IP&#xff0c;作为二级路由器 这一次&#xff0c;咱们尝试用OpenWRT有线桥接上一级路由器的教程。 可能有小伙伴敏锐地发现了&am…

【JVM】GC调优(优化JVM参数)、性能调优

GC调优 GC调优的主要目标是避免由垃圾回收引起程序性能下降。 GC调优的核心指标 垃圾回收吞吐量&#xff1a;执行用户代码时间/&#xff08;执行用户代码时间 GC时间&#xff09;延迟&#xff1a;GC延迟 业务执行时间内存使用量 GC调优步骤 发现问题&#xff1a;通过监控…

论文研读|针对文生图模型的AIGC检测

前言&#xff1a;人工智能生成内容的鉴别&#xff08;AIGC检测&#xff09;算是当前的研究热点之一&#xff0c;本篇文章介绍几篇针对文生图模型的 AIGC 检测相关工作。 相关文章&#xff1a;AIGC溯源相关研究详见此篇文章 目录 1. Towards Universal Fake Image Detectors tha…

OneNote导出白色背景文件时将笔记墨迹转换颜色

今天用OneNote导出笔记时发现在文件上做的黑色墨迹笔记全部转成了白色。推测是因为onenote会根据背景色自动转换黑色和白色的墨迹&#xff0c;但是其他颜色好像导出的时候不会转换。 于是&#xff0c;我们首先要转换背景&#xff0c;将黑色背景转成白色背景&#xff0c; 然后将…

GNU Radio创建FFT、IFFT C++ OOT块

文章目录 前言一、GNU Radio官方FFT弊端二、创建自定义的 C OOT 块1、创建 OOT 模块2、创建 OOT 块3、修改 C 和 CMAKE 文件4、编译及安装 OOT 块 三、测试1、grc 图2、运行结果①、时域波形对比②、频谱图对比 四、资源自取 前言 GNU Radio 自带的 FFT 模块使用起来不是很方便…

OCC笔记:选择TopoDS_Shape顶点、边、面等等

1、通过AIS_InteractiveContext的函数访问当前选择的图形 hAISContext->InitSelected(); hAISContext->MoreSelected(); hAISContext->NextSelected()&#xff1b; hAISContext->SelectedShape()&#xff1b; 其中hAISContext->SelectedShape()通过StdSelect_…

项目管理【环境】过程

系列文章目录 【引论一】项目管理的意义 【引论二】项目管理的逻辑 【环境】概述 【环境】原则 【环境】过程 一、规划和管理项目的合规性 1.1 规划和管理项目的合规性 1.2 确认合规要求 1.3 审计&#xff1a;衡量合规的程度 二、项目管理计划和项目文件 2.1 项目管理计划和…

C语言 联合和枚举

目录 1. 联合体1.1 联合体类型的声明1.2 联合体变量的创建1.3 联合体的特点1.4 联合体在内存中的存储1.5 联合体使用举例 2. 枚举类型2.1 枚举类型的声明2.2 枚举变量的创建和初始化2.3 枚举类型的大小2.4 枚举类型的优点 正文开始 上次我们通过《C语言 结构体详解》学习了结构…

C语言 | Leetcode C语言题解之第66题加一

题目&#xff1a; 题解&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/ int* plusOne(int* digits, int digitsSize, int* returnSize){for(int i digitsSize - 1; i > 0; --i){digits[i] digits[i] 1;//最后元素1判断是不…

笔试狂刷--Day12(模拟 + 链表的公共节点 + dp)

大家好,我是LvZi,今天带来笔试狂刷--Day12(模拟 链表的公共节点 dp) 一.删除公共字符&#xff08;哈希&#xff09; 题目链接:删除公共字符&#xff08;哈希&#xff09; 分析: 分别读取俩个字符串,将第二个字符串存储到set之中,再遍历第一个字符串,删除公共字符 代码: …

《网络安全技术 网络安全众测服务要求》

近日&#xff0c;全国网络安全标准化技术委员会发布《网络安全技术 网络安全众测服务要求》&#xff08;GB/T 43741-2024&#xff0c;以下简称“众测服务要求”&#xff09;&#xff0c;并将在2024年11月1日正式实施。 《众测服务要求》确立了网络安全众测服务的角色及其职责&…

ruoyi漏洞总结

若依识别 黑若依 :icon hash"-1231872293 绿若依 :icon hash"706913071” body" 请通过前端地址访 " body" 认证失败&#xff0c;无法访问系统资源 " 如果页面访问显示不正常&#xff0c;可添加默认访问路径尝试是否显示正常 /login?redi…

Dashboard 介绍

Dashboard 介绍 一、K8S Dashboard简介 简单的说&#xff0c;K8S Dashboard是官方的一个基于WEB的用户界面&#xff0c;专门用来管理K8S集群&#xff0c;并可展示集群的状态。K8S集群安装好后默认没有包含Dashboard&#xff0c;我们需要额外创建它 二、RABC简介 还是那句话&a…

MLP手写数字识别(1)-MNIST数据集下载与可视化(tensorflow)

1.下载与查看MNIST数据集 from keras.datasets import mnist(x_train_image,y_train_label),(x_test_image,y_test_label) mnist.load_data() print("train images:",x_train_image.shape) print("test images:",x_test_image.shape) print("train …

LeetCode 面试经典150题 28.找出字符串中第一个匹配项的下标

题目&#xff1a;给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 思路&#xff1a;暴力&#xff08;…

RabbitMQ知识点总结和复习

之前项目中用到RabbitMQ的场景主要是订单信息的传递&#xff0c;还有就是利用RabbitMQ的死信队列属性设置&#xff0c;实现延迟队列效果&#xff0c;实现超时支付取消功能&#xff0c;以及在两个不同项目中传递数据等场景。 最近几年的工作中都是一直用的RabbitMQ&#xff0c;…

Springboot+MybatisPlus入门案例(postman测试)

一、项目框架 pom.xml依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apac…

基于php+mysql+html图书管理系统(含实训报告)

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、Php、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

任何内核都无法启动解决方案

背景 实验中不停编译新的内核又懒得删除了&#xff0c;于是乎在编译到第9716个内核后&#xff0c;无法启动了。 报错如下&#xff1a; 主要是这句报错&#xff1a; 解决方案 ubuntu linux开机进入不了系统的解决办法 进入Recovery Mode打开root shell失败&#xff1a; 一…