服务网关GateWay原理

news2024/11/24 1:16:13

文章目录

      • 自动装配核心类
      • 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.coloradmin.cn/o/1639992.html

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

相关文章

C语言二叉树和堆

二叉树基础知识&#xff1a; 1.栈、队列和顺序表都是线性结构 但是二叉树不是&#xff0c;二叉树是多分支结构 2.任何一棵树都可以拆分为子树和根节点&#xff0c;许多二叉树的相关问题都是用分治的思想进行函数的递归进行解决。 例&#xff1a;前序&#xff0c;中序&#x…

社交媒体数据恢复:推特、Twitter

推特&#xff08;Twitter&#xff09;数据恢复&#xff1a;如何找回丢失的内容 随着社交媒体的普及&#xff0c;越来越多的人开始使用推特&#xff08;Twitter&#xff09;来分享生活点滴、发表观点和获取信息。然而&#xff0c;有时候我们会不小心删除了重要的推文&#xff0…

【牛客网】排列计算

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 如果直接涂色来计算单点权重&#xff0c;2e5*2e5必然超时。 所以用差分进行优化。 3. 代码实现 #include<bits/stdc.h> using name…

漏洞扫描神器:Nessus 保姆级教程(附破解步骤)

一、介绍 Nessus是一款广泛使用的网络漏洞扫描工具&#xff0c;用于发现和评估计算机系统和网络中的安全漏洞。它是一款功能强大的商业工具&#xff0c;由Tenable Network Security开发和维护。 以下是Nessus的一些主要特点和功能&#xff1a; 1. 漏洞扫描&#xff1a;Nessu…

来一篇错题集(虽然简单吧)

一.Assembly via Remainders #include<bits/stdc.h> using namespace std; typedef long long ll; int a[2000]; int b[2000]; int main(){int t;cin>>t;while(t--){int n;cin>>n;for(int i1;i<n-1;i){cin>>b[i];}int x1000000000;//使用1000000000…

241 基于matlab的Dijkstra算法进行路径规划

基于matlab的Dijkstra算法进行路径规划。可根据实际情况输入障碍物和起止点坐标信息&#xff1b; 输出避碰最短路径&#xff1b; 能够利用切线图算法对障碍物区域进行环境建模&#xff0c;设置障碍物的位置和区域。利用Dijkstra算法进行路径规划。程序已调通&#xff0c;可直接…

uniapp实现点击事件跳转页面

首先定义一个点击事件 这里采用的vue3的写法&#xff0c;然后写上触发事件后要跳转的路径 function jump() {uni.switchTab({url:/pages/bangong/index})} 到这里就简单的实现uniapp的点击跳转页面了

类和对象【四】运算符重载

文章目录 运算符重载的概念运算符重载&#xff08;函数&#xff09;返回值类型&#xff1a;任意类型函数名&#xff1a;operator已有操作符 运算符重载&#xff08;函数&#xff09;的特点和注意点3个比较特殊的运算符重载赋值运算符&#xff08;&#xff09;重载返回值类型和返…

C++奇迹之旅:C++内存管理的机制(进阶篇)

文章目录 &#x1f4dd;new和delete操作自定义类型&#x1f320; operator new与operator delete函数&#x1f309;operator new与operator delete函数 &#x1f320;new和delete的实现原理&#x1f309;内置类型&#x1f309;自定义类型 &#x1f320;定位new表达式(placement…

【CTF MISC】XCTF GFSJ0512 give_you_flag Writeup(图像处理+QR Code识别)

give_you_flag 菜狗找到了文件中的彩蛋很开心&#xff0c;给菜猫发了个表情包 解法 图片的最后一帧好像闪过了什么东西。 用 Photoshop 打开&#xff0c;检查时间轴。 找到一张二维码&#xff0c;但是缺了三个角&#xff08;定位图案&#xff09;&#xff0c;无法识别。 找一…

单片机编程实例400例大全(100-200)

今天继续分享单片机编程实例第100-200例。 今天的实例会比前面100复杂一些&#xff0c;我大概看了下&#xff0c;很多都具备实际产品的参考价值。 今天继续分享单片机编程实例第100-200例。 今天的实例会比前面100复杂一些&#xff0c;我大概看了下&#xff0c;很多都具备实际…

学习经验分享【34】目标检测算法常见疑问(持续更新)

很多同学在学习深度学习算法过程中会遇到各种各样的问题,本博文对大家普遍存在的问题进行答疑,包括写论文常需要的指标FPS如何计算得到,怎么判断模型是否收敛等问题。如有问题可通过以下方式联系我。 一、Fuse指的是什么 Fuse是指模型的一些模块进行融合,最常见的就是conv和…

AI图书推荐:如何使用ChatGPT来提升逻辑分析能力

在一个日益由数据和技术驱动的世界中&#xff0c;进行逻辑思考和做出明智决策的能力比以往任何时候都更为关键。逻辑分析构成了理性思考的基础&#xff0c;引导我们穿越复杂问题&#xff0c;并帮助我们得出合理的结论。随着人工智能&#xff08;AI&#xff09;的出现&#xff0…

普通电机与变频电机区别

普通电机和变频电机之间的区别&#xff1a; 1. 设计和构造&#xff1a; - 普通电机&#xff1a;设计用于在恒定的电源频率和电压下工作&#xff0c;通常具有固定的转速和功率。 - 变频电机&#xff1a;专门设计用于与变频器配合使用&#xff0c;能够在变化的频率和电压下运行&…

07 - 步骤 javaScript代码

简介 JavaScript 代码是通过 JavaScript 脚本步骤来执行 JavaScript 脚本的一种方式。这允许用户在 Kettle 的数据流程中使用 JavaScript 编写自定义的脚本逻辑&#xff0c;用于数据处理、转换、计算等操作。 使用 场景 我需要在数据流加一个字段 createTime 当前时间&…

2024年 Java 面试八股文——SpringMVC篇

目录 1.简单介绍下你对springMVC的理解? 2.说一说SpringMVC的重要组件及其作用 3.SpringMVC的工作原理或流程 4.SpringMVC的优点 5.SpringMVC常用注解 6.SpringMVC和struts2的区别 7.怎么实现SpringMVC拦截器 8.SpringMvc的控制器是不是单例模式&#xff1f;如果是&am…

Matlab|二阶锥松弛在配电网最优潮流计算中的应用

目录 一、主要内容 二、部分代码 三、程序代码 四、下载链接 一、主要内容 最优潮流计算是电网规划、优化运行的重要基础。首先建立了配电网全天有功损耗最小化的最优潮流计算模型&#xff1b;其次结合辐射型配电网潮流特点建立支路潮流约束&#xff0c;并考虑配电网中的可…

C++ 多态(一)

一、多态定义 同一种操作作用于不同的对象时&#xff0c;可以产生不同的行为。在面向对象编程中&#xff0c;多态性是指通过继承和重写实现的&#xff0c;同一个方法在不同的子类中可以表现出不同的行为。多态性可以提高代码的灵活性和可扩展性&#xff0c;使得程序更易于维护…

LeetCode 131 —— 分割回文串

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 首先&#xff0c;按照 LeetCode 5——最长回文子串 中的思路&#xff0c;我们先求出 d p dp dp&#xff0c;这样我们就知道了所有的子串是否是回文子串。 然后&#xff0c;我们进行一个 dfs 搜索&#xff0c;起…

汇报进度26届cpp,目前来说之后的规划,暑假打算回家10天就留校沉淀了

汇报一下进度吧&#xff0c;26双非菜鸡&#xff0c;cpper. 但目前学了一些go &#xff0c;辅修吧&#xff0c;距离发的上个动态已经过去3个月了&#xff0c;真的觉得找实习时间来不及&#xff0c;现在leetcode 100多道题&#xff0c;前几天蓝桥杯整了个省二&#xff0c;把OS和…