服务网关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。
自动装配类 | 作用 |
---|---|
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的初始化,它的类声明如下:
@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初始化器内部依赖。
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 类似。
框架 | 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方法
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;});
}
该方法主要做两件事
- 根据路由规则获取对应的Handler
- 如果请求为跨域,则为该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);
}
上面就是适配器的主要流程,主要分五步
- 从ServerWebExchange网关上下文中获取匹配到的路由信息
- 获取路由信息中配置的过滤器局部过滤器以及全局过滤器,合并到一起并排序
- 创建责任链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 客户端。
相关文章:

服务网关GateWay原理
文章目录 自动装配核心类GatewayAutoConfigurationDispatcherHandler请求处理阶段apply方法httpHandler#handle方法WebHandler#handle方法DispatchHanlder#handle方法第一步 getHandler获取请求映射第二步 invokeHandler 请求适配第三步 handleResult请求处理总结 上一篇博文我…...

第12节 第二种shellcode编写实战(1)
我最近在做一个关于shellcode入门和开发的专题课👩🏻💻,主要面向对网络安全技术感兴趣的小伙伴。这是视频版内容对应的文字版材料,内容里面的每一个环境我都亲自测试实操过的记录,有需要的小伙伴可以参考…...

在Ubuntu上安装Anaconda并配置远程访问Jupyter
安装 下载Anaconda的.sh文件后,上传到服务器,然后进行安装: chmod x anaconda.sh ./anaconda.sh创建虚拟环境 可以指定Python版本创建虚拟环境: conda create --name langchain python3.11.7 conda activate langchain conda …...

格雷希尔GripSeal:E10系列低压信号电测试连接器,应用于新能源汽车的DCR测试和EOL测试
新能源车的电驱动、电池包等都有一些信号接口,从几针到几十针不等,而且每种接口都有独特的电性能要求,这些接口在电池包进DCR测试或是EOL测试时,为了满足这些信号接口的需求,我们设计了E10系列信号针快速接头ÿ…...

飞跨电容型的三电平(FC-NPC)逆变器simulink仿真模型
本人搭建了飞跨电容型的三电平逆变器simulink仿真模型,相较于二极管钳位型三电平逆变器而言,钳位二极管变为飞跨的电容。采用SPWM调制和均流均压控制,通过搭建仿真模型得到三电平波形。 三电平拓扑中的飞跨电容是指在电路的输出端使用电容来实…...

前端Sass使用详解,看这篇就够了
Sass(Syntactically Awesome Style Sheets)是一种CSS预处理器,它被设计为改善CSS的可读性和实用性。Sass使用类似于CSS的语法,但增加了变量、嵌套、混合(mixins)、函数等功能,使得编写CSS更加高…...

用js操作dom节点的一些方法
一、获取节点 document.getElementById(id); 返回拥有指定 id 的第一个节点 document.getElementsByName(name); 返回带有指定名称的节点集合 document.getElementsByTagName(tagname); 返回带有指定标签名的节点集合 document.getElementsByClassName(classname); 返回带有…...

electron 中拦截内嵌页面 beforeunload 的弹窗提示
window 的 beforeunload 事件提示在electron 不兼容,弹窗提示不出来,还会导致莫名其妙的假死问题,下面记录一下解决方法。 1. 如果仅需要拦截弹窗: win.webContents.on(will-prevent-unload, (event) > {event.preventDefault(…...

hcip-datacom英文词汇积累简述3
序号 词汇 中文 1 port link-type access 端口链路类型为接入 2 batch 批量 3 vlan batch 2 3 虚拟局域网批量2和3 4 Default 默认 5 port default vlan 2 端口默认虚拟局域网2 6 trunk 主干 7 port link-type trunk 端口链路类型为主干 8 allow-pass 全部过关 9 port trunk al…...

什么是新能源汽车热管理?
前言 新能源汽车热管理是指针对电动汽车等新型动力系统所涉及的热量控制和调节技术,其包括散热、冷却、加热、温度控制等方面。在新能源汽车中,电池、电动机、控制器等部件都会产生一定的热量,如果不进行有效的热管理,将会影响汽…...

iOS plist文件增删改查
一. plist简介 plist文件,即属性列表文件,全名是Property List,这种文件的扩展名为.plist,因此,通常被叫做plist文件。它是一种用来存储串行化后的对象的文件,在iOS开发中通常用来存储用户设置,…...

docker安装与重装
docker安装与重装 docker安装 https://blog.csdn.net/lyqhf153/article/details/79585976 参考上面的方式 cat /etc/issueuname -r uname -acat /proc/versiondf -hyum list docker-ce --showduplicates | sort -r 查看docker-ce的版本列表sudo yum install -y docker 没有…...

武汉星起航引领跨境新浪潮,一站式解决方案助力卖家驰骋亚马逊
在全球化浪潮下,跨境电商已成为外贸发展的新引擎,为无数创业者提供了全新的商业机遇。而在这场跨境电商的浪潮中,武汉星起航电子商务有限公司以其专业的一站式解决方案,成为众多创业者和卖家的得力助手,引领着他们成功…...

在做题中学习(56):二维前缀和模板
【模板】二维前缀和_牛客题霸_牛客网 (nowcoder.com) 理解题意: 要求的是(x1,y1) - (x2,y2)这段区间的和。 解法:二维前缀和 1. 和一维前缀和一样,需要有一个同等规模的dp数组,用来保存一段连续区域的和。 在二维dp中࿰…...

驾驭多云环境,加速AI创新丨Animbus Cloud 8.3.0 算力调度平台升级发布
大模型开启全球新一轮AI浪潮,伴随算力规模的爆发增长以及计算技术的多元创新,需要更稳定、高效、敏捷的异构计算基础设施,才能充分发挥对算力能力的重要支撑。 作为开放智能云边架构引领者,九州未来凭借多年的技术积累、实践沉淀…...

JavaScript异步编程——02-Ajax入门和发送http请求
同步和异步回顾 同步和异步的简单理解 同步:必须等待前面的任务完成,才能继续后面的任务。 异步:不受当前任务的影响。 拿排队举例: 同步:在银行排队时,只有等到你了,才能够去处理业务。 异…...

湖仓一体 - Apache Arrow的那些事
湖仓一体 - Apache Arrow的那些事 Arrow是高性能列式内存格式标准。它的优势:高效计算:所有列存的通用优势,CPU缓存友好、SIMD向量化计算友好等;零序列化/反序列化:arrow的任何数据结构都是一段连续的内存,…...

常用的启发式算法:探索问题解决的智慧之道
启发式算法是一种通过启发式信息来引导搜索的算法,常用于解决那些在合理时间内难以找到最优解的问题。本文将介绍几种常用的启发式算法,包括贪心算法、遗传算法和模拟退火算法,并提供Java代码实现及测试,帮助读者深入理解这些算法…...

docker Harbor私有仓库部署管理
搭建本地私有仓库,但是本地私有仓库的管理和使用比较麻烦,这个原生的私有仓库并不好用,所以我们采用harbor私有仓库,也叫私服,更加人性化。 一、什么是Harbor Harbor是VWware 公司开源的企业级Docker Registry项…...

序列化的不同格式:JSON、XML、TOML、CSON、YAML
前言 这篇文章参考于知乎,进行了一些总结。 正文 首先什么是序列化,数据序列化是从一个系统获取一些信息,将其转换为其它系统可以读取的格式,然后将其传递给其它系统的过程。也就是可以让不同系统“通信”。 序列化需要满足两…...

Mapreduce | 案例
根据提供的数据文件【test.log】 数据文件格式:姓名,语文成绩,数学成绩,英语成绩 完成如下2个案例: (1)求每个学科的平均成绩 (2)将三门课程中任意一门不及格的学生过滤出来 (1)求每…...

U盘文件剪切丢失怎么办?揭秘原因并给出恢复方法
在日常生活和工作中,U盘已成为我们不可或缺的数据存储和传输工具。但有时候,我们在对U盘中的文件进行剪切操作时,会遇到文件丢失的情况。这种突如其来的数据消失往往会让人感到惊慌和困惑。那么,为什么U盘剪切时文件会丢失呢&…...

软件设计师考试---访问控制列表、堆,栈和堆栈、防火墙、数据流图、嵌入式操作、绑定方式、uml、模式、传输协议
访问控制列表 访问控制列表(Access Control List,ACL) 是一种用于控制对资源(如文件、目录、网络资源等)访问权限的方法。ACL是在计算机安全领域广泛使用的概念,它允许系统管理员定义哪些用户或系统进程有…...

vlock工具:锁定Linux终端的安全智能方法
虚拟控制台是 Linux 非常重要的功能,它们为系统用户提供 shell 提示,以非图形设置方式使用系统,该设置只能在物理机上使用,而不能远程使用。 用户只需从一个虚拟控制台切换到另一个虚拟控制台即可同时使用多个虚拟控制台会话。 …...

【Linux】Docker 安装部署 Nacos
个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ 【Linux】Docker 安装部署 Nacos docker搜索na…...

纯血鸿蒙APP实战开发——阅读翻页方式案例
介绍 本示例展示手机阅读时左右翻页,上下翻页,覆盖翻页的功能。 效果图预览 使用说明 进入模块即是左右翻页模式。点击屏幕中间区域弹出上下菜单。点击设置按钮,弹出翻页方式切换按钮,点击可切换翻页方式。左右翻页方式可点击翻…...

如何从Mac电脑恢复任何删除的视频
Microsoft Office是包括Mac用户在内的人们在世界各地创建文档时使用的最佳软件之一。该软件允许您创建任何类型的文件,如演示文稿、帐户文件和书面文件。您可以使用 MS Office 来完成。所有Microsoft文档都可以在Mac上使用。大多数情况下,您处理文档&…...

【Halcon 内存泄漏记录 - C#】
Halcon 内存泄漏记录 - C# 1. Bitmap 转 HImage2. new 之后要Dispose()3. 切换配方后,内存会增加4. Parallel.For 嵌套Parallel.For, 会出现问题5. 图像预处理使用需要注意不能直接在原有变量上赋值 1. Bitmap 转 HImage 由于Bitmap 在转化时使用Bitmap…...

MT8370_联发科MTK8370(Genio 510)芯片性能规格参数
MT8370芯片是一款利用超高效的6nm制程工艺打造的边缘AI平台,具有强大的性能和功能。这款芯片集成了六核CPU(2x2.2 GHz Arm Cortex-A78 & 4x2.0 GHz Arm Cortex-A55)、Arm Mali-G57 MC2 GPU、集成的APU(AI处理器)和DSP,以及一个HEVC编码加速引擎&…...

【Qt 学习笔记】Qt常用控件 | 多元素控件 | Table Widget的说明及介绍
博客主页:Duck Bro 博客主页系列专栏:Qt 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ Qt常用控件 | 多元素控件 | Table Widget的说明及介绍 文章编号&#…...