服务网关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) {
        //获取所有符合条件的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 类似。

框架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>>{
	@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方法
	@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

@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 根据路由规则获取对应的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实现类获取路由信息

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所有跟网关有关的适配器为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

@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) {
    //获取真实的业务请求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。

//获取符合了条件的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.mfbz.cn/a/591490.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【刷题篇】回溯算法floodfill(七)

文章目录 1、太平洋大西洋水流问题2、扫雷游戏3、衣橱整理 1、太平洋大西洋水流问题 有一个 m n 的矩形岛屿&#xff0c;与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界&#xff0c;而 “大西洋” 处于大陆的右边界和下边界。 这个岛被分割成一个由若干方形…

Python 全栈体系【四阶】(三十九)

第五章 深度学习 八、目标检测 3. 目标检测模型 3.2 YOLO 系列 3.2.4 YOLOv4&#xff08;2020 年 4 月&#xff09; YOLOv4 将最近几年 CV 界大量的研究成果集中在一套模型中&#xff0c;从检测速度、精度、定位准确率上有了明显改善&#xff08;相对于 YOLOv3&#xff0c…

基于Springboot的家具网站

基于SpringbootVue的家具网站设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 商家 家具信息 家居资讯 后台管理 后台首页 用户管理 商家管理 家具类型管理 家具…

ASV1000视频监控平台:通过SDK接入海康网络摄像机IPC

目录 一、为何要通过SDK接入海康网络摄像机 &#xff08;一&#xff09;海康网络摄像机的SDK的功能 1、视频采集和显示 2、视频存储 3、视频回放 4、报警事件处理 5、PTZ控制 6、自定义设置 7、扩展功能 &#xff08;二&#xff09;通过SDK接入的好处&#xff08;相对…

JavaEE初阶-多线程易忘点总结

文章目录 1.PCBPID文件描述符表内存指针状态上下文优先级记账信息tgid 2.线程与进程的区别3.sleep和interrupt方法的关系变量终止线程interrupt方法终止线程 4.线程状态5.出现线程不安全的原因线程在系统中是随即调度&#xff0c;抢占式执行的。多个线程修改同一个变量线程针对…

Adobe 更新 Firefly Image 3 图像生成模型

一个工具或者模型&#xff0c;对于初次使用的人来说&#xff0c;易用性和超出预期的效果很能吸引使用者&#xff0c;suno和mj在这方面我感觉确实不错&#xff0c;第一次使用感觉很惊艳。 Adobe 更新 Firefly Image 3 图像生成模型&#xff0c;我用了mj的提示词&#xff0c;最后…

列转行(spark 与presto语法)

一、Presto 语法 原始数据&#xff1a; 期望数据&#xff1a; 代码&#xff1a; SELECT info, value FROM ( select 张三 as name,18 as age,男 as gender,清华 as schoolunion allselect 李四 as name,18 as age,男 as gender,清华 as school ) as a CROSS JOIN UNNEST(…

关于YOLO8学习(六)安卓部署ncnn模型--图片检测

前文 关于YOLO8学习(一)环境搭建,官方检测模型部署到手机 关于YOLO8学习(二)数据集收集,处理 关于YOLO8学习(三)训练自定义的数据集 关于YOLO8学习(四)模型转换为ncnn 关于YOLO8学习(五)安卓部署ncnn模型–视频检测 简介 前文第五章,讲述了部署自定义模型后,进…

Java--方法的使用

1.1什么是方法 方法顾名思义就是解决问题的办法&#xff0c;在程序员写代码的时候&#xff0c;会遇到很多逻辑结构一样&#xff0c;解决相同问题时&#xff0c;每次都写一样的代码&#xff0c;这会使代码看起来比较绒余&#xff0c;代码量也比较多&#xff0c;为了解决这个问题…

分拣机器人也卷的飞起来了

导语 大家好&#xff0c;我是智能仓储物流技术研习社的社长&#xff0c;老K。专注分享智能仓储物流技术、智能制造等内容。 新书《智能物流系统构成与技术实践》 智能制造-话题精读 1、西门子、ABB、汇川&#xff1a;2024中国工业数字化自动化50强 2、完整拆解&#xff1a;智能…

foobar2000 for Mac:卓越音乐播放器

当您在寻找一款音质卓越、功能丰富的音频播放器时&#xff0c;foobar2000 for Mac无疑是您的首选。它拥有简洁明了的界面设计&#xff0c;易于上手&#xff0c;同时支持多种音频格式&#xff0c;让您无需担心兼容性问题。 foobar2000 for Mac v2.6.4免激活版下载 foobar2000 fo…

匹配网络(Matching Networks)和原型网络(Prototypical Networks):区别详解

匹配网络&#xff08;Matching Networks&#xff09;和原型网络&#xff08;Prototypical Networks&#xff09; 匹配网络与原型网络&#xff1a;区别详解匹配网络&#xff08;Matching Networks&#xff09;核心特点&#xff1a;应用场景&#xff1a; 原型网络&#xff08;Pro…

威尔科克森秩和检验 (Wilcoxon rank-sum test)-- 代码实现

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

设计模式之业务代表模式

在编程江湖的风雨中漂泊多年&#xff0c;每当我遇到那些错综复杂的业务逻辑和系统交互&#xff0c;总有一个模式像一位忠诚的骑士&#xff0c;默默守护着我的代码城堡&#xff0c;那就是——业务代表模式&#xff08;Business Delegate Pattern&#xff09;。它不是最耀眼的明星…

一键实现在VS Code中绘制流程图

VS Code是一款常用的IDE&#xff0c;受到许多用户的欢迎和喜爱。而其较为出众的一点&#xff0c;就是较好的可拓展性&#xff0c;即丰富的插件应用&#xff0c;这些应用可以极大地提高生产效率&#xff0c;并优化日常使用。 流程图是一种直观的图示方法&#xff0c;可以用简明…

家庭用水安全新举措:保障自来水管和储水设施卫生

随着公众对家庭用水安全意识的提高&#xff0c;如何确保自来水管和楼顶储水罐的安全性和卫生已成为家庭生活中的重要议题。近期&#xff0c;专家针对此问题提出了一系列实用的注意事项和建议。 注意事项&#xff1a; 定期检查&#xff1a;专家强调&#xff0c;家庭应每季度至…

eNSP-静态路由配置

一、拓扑图搭建 二、主机ip、掩码、网关设置 pc1 pc2 三、路由器配置 1.AR1ip地址及路由配置 <Huawei>sys #进入系统视图 [Huawei]int g0/0/0 #进入0/0/0接口 [Huawei-GigabitEthernet0/0/0]ip address 192.168.0.2 24 #设置ip地址 [Huawei-GigabitEthernet0/0/0]q #…

基础IO认识

回顾文件 我们之前认识文件只是在语言程度上理解&#xff0c;但是我们理解的不够彻底&#xff0c;要想真正理解文件要在os上理解。 简单代码认识 1 #include<stdio.h>2 int main(){3 FILE* fpfopen("log.txt","w");4 if(fpNULL){5 p…

浅析边缘计算技术

概念 边缘计算是一种分布式计算范式&#xff0c;它将计算任务和数据存储从中心化的云端推向网络的边缘&#xff0c;即设备或终端&#xff0c;以提高响应速度和降低网络带宽需求。在边缘计算中&#xff0c;数据在源头附近进行处理和分析&#xff0c;而不需要将所有数据传输到…

21世纪世界十大名人颜廷利:真正的幸福是快乐, 真正的理想是远航

真正的财富是分享, 真正的情感是珍藏; 真正的人生是奋斗, 真正的自由是飞翔; 真正的幸福是快乐, 真正的理想是远航… &#xff08;升命学说&#xff09; 21世纪东方哲学家思想家、科学家、当代中国教育界知名教授、专业周易起名改名字、易经姓名学专家、目前比较有影响力的人…