当前位置: 首页 > news >正文

Gateway源码分析:路由Route、断言Predicate、Filter

文章目录

    • 源码总流程图
    • 说明
    • GateWayAutoConfiguration
    • DispatcherHandler
    • getHandler()
    • handleRequestWith()
      • RouteToRequestUrlFilter
      • ReactiveLoadBalancerClientFilter
      • NettyRoutingFilter
    • 补充知识
      • 适配器模式
    • 详细流程图

源码总流程图

在线总流程图
在这里插入图片描述



说明

Gateway的版本使用的是4.0.0

Gateway的实现是基于WebFlux 、Reactor 、Netty



Gateway微服务的yml配置如下

  • Gateway的访问端口为8888
  • id为order_route的路由 uri为lb://mall-order
  • 为mall-order这个微服务定义了一个path路径的predicate断言;定义了三个filter
server:port: 8888
spring:application:name: mall-gateway#配置nacos注册中心地址cloud:nacos:discovery:server-addr: nacos.mall.com:8848username: nacospassword: nacosgateway:#设置路由:路由id、路由到微服务的uri、断言routes:- id: user_route   #路由ID,全局唯一uri: lb://mall-user  #lb 整合负载均衡器ribbon,loadbalancerpredicates:- Path=/user/**   # 断言,路径相匹配的进行路由- id: order_route  #路由ID,全局唯一# 测试 http://localhost:8888/order/findOrderByUserId/1uri: lb://mall-order  #lb 整合负载均衡器loadbalancerpredicates:- Path=/order/**   # 断言,路径相匹配的进行路由#配置过滤器工厂filters:- AddRequestHeader=X-Request-color, red  #添加请求头- AddRequestParameter=color, blue  # 添加请求参数- CheckAuth=hushang,#自定义过滤器工厂



Gateway的工作原理就是下面这一张图

在这里插入图片描述



在开始看Gateway的源码之前,我们回忆一下SpringMVC的实现原理:

  1. DispatchServlet#doDispatch作为Springmvc的入口
  2. HandlerMapper 路由匹配 —> 找到Handler
  3. 通过handler 找的适配的 HandlerAdapter
  4. HandlerAdapter#handle方法执行



而在Gateway的源码中:

  • DispatcherHandler#handle作为入口
  • HandlerMapping 路由匹配 --> 断言predicate匹配 RoutePredicateHandlerMapping#getHandlerInternal,找到路由Route对象
  • 返回FilteringWebHandler
  • HandlerAdapter 适配器 SimpleHandlerAdapter#handle 处理WebHandler
  • 进入到org.springframework.cloud.gateway.handler.FilteringWebHandler#handle —> filterChain处理



Flux和Mono的概念

Reactor学习文档

Flux:

在这里插入图片描述



Mono:

在这里插入图片描述



GateWayAutoConfiguration

在这里插入图片描述



在GatewayAutoConfiguration自动配置类中,它配置了很多bean对象,常见的就比如:

// 保存我们配置文件中关于网关路由相关的所有配置
// GatewayProperties保存了List<RouteDefinition>
// 而RouteDefinition就是每一个路由对象,保存了id、uri、断言集合List<PredicateDefinition>、Filter集合List<FilterDefinition>
@Bean
public GatewayProperties gatewayProperties() {return new GatewayProperties();
}// Path路径匹配的断言工厂,断言相关的bean都是以RoutePredicateFactory结尾
@Bean
@ConditionalOnEnabledPredicate
public PathRoutePredicateFactory pathRoutePredicateFactory() {return new PathRoutePredicateFactory();
}// 添加请求头的Filter,一般都是以GatewayFilterFactory
@Bean
@ConditionalOnEnabledFilter
public AddRequestHeaderGatewayFilterFactory addRequestHeaderGatewayFilterFactory() {return new AddRequestHeaderGatewayFilterFactory();
}// 全局过滤器,把我们访问网关的url转换为路由中配置的uri
// http://localhost:8888/order/findOrderByUserId/1  --->  lb://mall-order/order/findOrderByUserId/1
@Bean
@ConditionalOnEnabledGlobalFilter
public RouteToRequestUrlFilter routeToRequestUrlFilter() {return new RouteToRequestUrlFilter();
}



GateWayAutoConfiguration配置的主要bean

类名说明
GatewayPropertiesgateway属性配置类
PropertiesRouteDefinitionLocator操作GatewayProperties对象,返回Flux<RouteDefinition>
RouteDefinitionRouteLocator将RouteDefinition转换为Route
RoutePredicateHandlerMappingGateway的HandlerMapping,匹配请求对应的Route,返回FilteringWebHandler
XXXRoutePredicateFactory路由断言工厂的bean
XXXGatewayFilterFactory局部Filter
GlobalFilter实现类全局Filter



DispatcherHandler

DispatcherHandler#handle作为我们查看Gateway源码的入口

  • 请求request和响应response实例会被封装为ServerWebExchange
  • 核心方法就是return语句
@Override
public Mono<Void> handle(ServerWebExchange exchange) {if (this.handlerMappings == null) {return createNotFoundError();}if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {return handlePreFlight(exchange);}return Flux.fromIterable(this.handlerMappings) // 遍历所有的HandlerMapper.concatMap(mapping -> mapping.getHandler(exchange)) // 调用每一个HandlerMapper,能否找到Handler.next() // 继续遍历下一个HandlerMapper.switchIfEmpty(createNotFoundError()) // 如果HandlerMapper遍历完后都没有Handler,那么要抛异常了.onErrorResume(ex -> handleDispatchError(exchange, ex)).flatMap(handler -> handleRequestWith(exchange, handler)); // 如果找到Handler,那就去通过HandlerAdapter去调用Handler
}



fromIterable()方法的作用就是就是遍历Gateway所有的HandlerMapper,我们这里肯定最终是使用的RoutePredicateHandlerMapping这个路由断言的

在这里插入图片描述

我们接下来继续往下,遍历各个HandlerMapper,并调用mapping.getHandler(exchange)方法,这里最终会调用至RoutePredicateHandlerMapping类的getHandlerInternal()方法中,经过断言匹配后,返回一个FilteringWebHandler对象。该方法接下来会详细介绍。



中间这几行其实主要就是如果我当前往Gateway的请求,通过路由断言没有匹配上,那么就会抛异常

.next()
.switchIfEmpty(createNotFoundError())
.onErrorResume(ex -> handleDispatchError(exchange, ex))



经过路由断言匹配,得到一个WebHandler对象之后,会执行handleRequestWith(exchange, handler)方法,在该方法中会找一个与WebHandler匹配的HandlerAdapter来适配WebHandler对象,最终去调用WebHandler的



getHandler()

通过DispatcherHandler#handle方法中的.concatMap(mapping -> mapping.getHandler(exchange)) 这一行代码

进入到了AbstractHandlerMapping#getHandler

  • 遍历我们yml配置文件中所有定义的路由

  • 根据我们路由定义的断言Predicate规则去调用对应的断言工厂

  • 将匹配成功的路由保存至exchange对象中

    exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
    exchange.getAttributes().put(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR, routeId);

  • 断言匹配成功就返回一个WebHandler接口的实现类FilteringWebHandler对象

public Mono<Object> getHandler(ServerWebExchange exchange) {// 从这里我们就可以发现,通过getHandlerInternal(exchange)方法就能找Handler,之后的.map()方法中就直接return handler;return getHandlerInternal(exchange).map(handler -> {if (logger.isDebugEnabled()) {logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);}ServerHttpRequest request = exchange.getRequest();// 正常情况下这个if都不会进入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 (config != null) {config.validateAllowCredentials();}if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {return NO_OP_HANDLER;}}// 直接返回handlerreturn handler;});
}





我们这里就直接进入到RoutePredicateHandlerMapping类中

在这里插入图片描述

RoutePredicateHandlerMapping#getHandlerInternal的详细代码如下

protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {if (this.managementPortType == DIFFERENT && this.managementPort != null&& exchange.getRequest().getURI().getPort() == this.managementPort) {return Mono.empty();}exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());return Mono.deferContextual(contextView -> {exchange.getAttributes().put(GATEWAY_REACTOR_CONTEXT_ATTR, contextView);// 核心方法是lookupRoute(exchange),这里会去进行路由的校验,根据我们配置文件中定义的路由断言规则进行校验return lookupRoute(exchange).flatMap((Function<Route, Mono<?>>) r -> {// 下面几行代码就是操作exchange的属性// 上方的lookupRoute()方法中会添加GATEWAY_PREDICATE_ROUTE_ATTR,这里就进行移除exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);// 把当前路由对象添加进exchange对象中,之后的流程还会用到我们的路由对象exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);// 路由匹配成功,就直接返回WebHandler对象return Mono.just(webHandler);}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {// 当前请求没有任何一个路由匹配上的处理流程exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);if (logger.isTraceEnabled()) {logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");}})));});
}





再进入到RoutePredicateHandlerMapping#lookupRoute方法中

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {// 获取到我们yml配置文件中所有定义的路由,并进行遍历return this.routeLocator.getRoutes().concatMap(route -> Mono.just(route).filterWhen(r -> {// 添加GATEWAY_PREDICATE_ROUTE_ATTRexchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());// 调用当前路由对象中的断言的apply()方法,apply()方法中又是一些异步的处理流程// 这里就会根据我们配置文件中为路由配置的各个断言,去调用各个断言对象return r.getPredicate().apply(exchange);}).doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e)).onErrorResume(e -> Mono.empty())).next()// TODO: error handling.map(route -> {if (logger.isDebugEnabled()) {logger.debug("Route matched: " + route.getId());}validateRoute(route, exchange);return route;});
}

我当前gateway的配置文件中定义了两个路由

spring:cloud:gateway:#设置路由:路由id、路由到微服务的uri、断言routes:- id: user_route   #路由ID,全局唯一uri: lb://mall-user  #lb 整合负载均衡器ribbon,loadbalancerpredicates:- Path=/user/**   # 断言,路径相匹配的进行路由- id: order_route  #路由ID,全局唯一# 测试 http://localhost:8888/order/findOrderByUserId/1uri: lb://mall-order  #lb 整合负载均衡器loadbalancerpredicates:- Path=/order/**   # 断言,路径相匹配的进行路由

所以上面的方法会执行两次

在这里插入图片描述

在这里插入图片描述

因为return r.getPredicate().apply(exchange);又是一些异步调用,但我们从方法名就能看出来,这里根据我们yml配置文件中路由定义的断言去调用对应的断言对象。这就是各个断言工厂具体的实现了,接下来就以Path路径匹配的断言工厂来举例

class DefaultAsyncPredicate<T> implements AsyncPredicate<T> {private final Predicate<T> delegate;@Overridepublic Publisher<Boolean> apply(T t) { // 调用各个Predicate对象的test()方法return Mono.just(delegate.test(t));}//...
}





所以我们现在就直接去看path路径匹配的断言类PathRoutePredicateFactory

@Override
public Predicate<ServerWebExchange> apply(Config config) {final ArrayList<PathPattern> pathPatterns = new ArrayList<>();synchronized (this.pathPatternParser) {pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchTrailingSlash());config.getPatterns().forEach(pattern -> {PathPattern pathPattern = this.pathPatternParser.parse(pattern);pathPatterns.add(pathPattern);});}return new GatewayPredicate() {// 会进入到test()方法中@Overridepublic boolean test(ServerWebExchange exchange) {// 当前请求的uri路径  /order/findOrderByUserId/1PathContainer path = parsePath(exchange.getRequest().getURI().getRawPath());PathPattern match = null;for (int i = 0; i < pathPatterns.size(); i++) {// yml配置文件中的配置项  /order/**PathPattern pathPattern = pathPatterns.get(i);// 如果path匹配成功,那么match对象就不为nullif (pathPattern.matches(path)) {match = pathPattern;break;}}// 如果path匹配成功,那么match对象就不为null  。匹配成功的处理逻辑if (match != null) {traceMatch("Pattern", match.getPatternString(), path, true);PathMatchInfo pathMatchInfo = match.matchAndExtract(path);putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());//  match.getPatternString() 为   /order/**exchange.getAttributes().put(GATEWAY_PREDICATE_MATCHED_PATH_ATTR, match.getPatternString());String routeId = (String) exchange.getAttributes().get(GATEWAY_PREDICATE_ROUTE_ATTR);// 保存当前路由idif (routeId != null) {exchange.getAttributes().put(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR, routeId);}return true;}// path匹配不成,返回falseelse {traceMatch("Pattern", config.getPatterns(), path, false);return false;}}@Overridepublic Object getConfig() {return config;}@Overridepublic String toString() {return String.format("Paths: %s, match trailing slash: %b", config.getPatterns(),config.isMatchTrailingSlash());}};
}



handleRequestWith()

// DispatcherHandler#handleRequestWith
private Mono<Void> handleRequestWith(ServerWebExchange exchange, Object handler) {if (ObjectUtils.nullSafeEquals(exchange.getResponse().getStatusCode(), HttpStatus.FORBIDDEN)) {return Mono.empty();  // CORS rejection}if (this.handlerAdapters != null) {// 遍历所有的HandlerAdapterfor (HandlerAdapter adapter : this.handlerAdapters) {// 找能处理WebHandler类型的HandlerAdapter , 最终找到SimpleHandlerAdapterif (adapter.supports(handler)) {return adapter.handle(exchange, handler).flatMap(result -> handleResult(exchange, result));}}}return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}

如下图所示,找到SimpleHandlerAdapter这个

在这里插入图片描述



SimpleHandlerAdapter的详细代码如下所示

public class SimpleHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return WebHandler.class.isAssignableFrom(handler.getClass());}@Overridepublic Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {// 强转WebHandler webHandler = (WebHandler) handler;// getHandler()方法返回了一个FilteringWebHandler对象,这里就调用它的handle()方法Mono<Void> mono = webHandler.handle(exchange);// 返回一个空对象return mono.then(Mono.empty());}}



FilteringWebHandler#handle方法

@Override
public Mono<Void> handle(ServerWebExchange exchange) {// getHandler()方法中存入了Route路由对象,这里取出来,该对象保存着我们yml配置文件中的配置Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);// 取出我们yml配置文件中 该路由的局部Filter,我这里有三个,一个添加请求头、一个添加请求参数、一个自定义的List<GatewayFilter> gatewayFilters = route.getFilters();// 全局FilterList<GatewayFilter> combined = new ArrayList<>(this.globalFilters);// 将局部Filter和全局Filter存入一个集合中combined.addAll(gatewayFilters);// 排序AnnotationAwareOrderComparator.sort(combined);if (logger.isDebugEnabled()) {logger.debug("Sorted gatewayFilterFactories: " + combined);}// 调用各自的filter()方法return new DefaultGatewayFilterChain(combined).filter(exchange);
}



在这里插入图片描述

[GatewayFilterAdapter{RemoveCachedBodyFilter@5cfed0ba}, order = -2147483648]"
[GatewayFilterAdapter{AdaptCachedBodyGlobalFilter@22a6d75c}, order = -2147482648]"
[GatewayFilterAdapter{NettyWriteResponseFilter@691567ea}, order = -1]"
[GatewayFilterAdapter{ForwardPathFilter@28be7fec}, order = 0]"
// 三个局部Filter
[[AddRequestHeader X-Request-color = 'red'], order = 1]"
[[AddRequestParameter color = 'blue'], order = 2]"
[com.tuling.mall.gateway.filter.CheckAuthGatewayFilterFactory$$Lambda$980/0x0000014b3c649390@54f513cb, order = 3]"
// 路由转换 把http://localhost:8888/order/findOrderByUserId/1  --->  lb://mall-order/order/findOrderByUserId/1
[GatewayFilterAdapter{RouteToRequestUrlFilter@5c8d58ed}, order = 10000]"
// 根据lb://前缀过滤处理,使用serviceId选择一个服务实例,从而实现负载均衡
[GatewayFilterAdapter{ReactiveLoadBalancerClientFilter@437ed416}, order = 10150]"
[GatewayFilterAdapter{LoadBalancerServiceInstanceCookieFilter@11f23038}, order = 10151]"
[GatewayFilterAdapter{WebsocketRoutingFilter@26f0141}, order = 2147483646]"
// 发送netty 请求
[GatewayFilterAdapter{NettyRoutingFilter@de77146}, order = 2147483647]"
[GatewayFilterAdapter{ForwardRoutingFilter@6a567f7b}, order = 2147483647]"



接下来就挑几个重要的全局GlobalFilter来分析

RouteToRequestUrlFilter

路由转换 把http://localhost:8888/order/findOrderByUserId/1?color=blue —> lb://mall-order/order/findOrderByUserId/1?color=blue

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 从exchange中取路由Route对象Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);if (route == null) {return chain.filter(exchange);}log.trace("RouteToRequestUrlFilter start");// 取当前请求uri : http://localhost:8888/order/findOrderByUserId/1?color=blueURI uri = exchange.getRequest().getURI();boolean encoded = containsEncodedParts(uri);// 路由对象中保存的uri,也就是我们在yml文件中配置的值:   lb://mall-orderURI routeUri = route.getUri();if (hasAnotherScheme(routeUri)) {exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());routeUri = URI.create(routeUri.getSchemeSpecificPart());}// 如果我们在yml文件中配置的uri,即不是lb开头并且host还为null,那么就抛异常if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {throw new IllegalStateException("Invalid host: " + routeUri.toString());}// 转换结果为:  lb://mall-order/order/findOrderByUserId/1?color=blueURI mergedUrl = UriComponentsBuilder.fromUri(uri).scheme(routeUri.getScheme()).host(routeUri.getHost()).port(routeUri.getPort()).build(encoded).toUri();// 存入exchange中,之后的LoadBalancer全局Filter中会用到exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);return chain.filter(exchange);
}



ReactiveLoadBalancerClientFilter

解析lb://服务名,去服务注册中心获取服务实例instance,并通过负载均衡算法选择一个具体的instance

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 此时的url是这个样子:   lb://mall-order/order/findOrderByUserId/1?color=blueURI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {return chain.filter(exchange);}addOriginalRequestUrl(exchange, url);if (log.isTraceEnabled()) {log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);}// 再获取一遍:lb://mall-order/order/findOrderByUserId/1?color=blueURI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);// 就是服务名:    mall-orderString serviceId = requestUri.getHost();Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),RequestDataContext.class, ResponseData.class, ServiceInstance.class);// 请求信息 封装成一个对象DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(new RequestDataContext(new RequestData(exchange.getRequest()), getHint(serviceId)));// 调用choose()方法return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {// 服务注册中心的响应response,获取server实例对象ServiceInstance retrievedInstance = response.getServer();// 值为http://localhost:8888/order/findOrderByUserId/1?color=blueURI uri = exchange.getRequest().getURI();String overrideScheme = retrievedInstance.isSecure() ? "https" : "http";if (schemePrefix != null) {overrideScheme = url.getScheme();}// 服务实例DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(retrievedInstance,overrideScheme);// 最终通过上面的服务实例,修改之后的请求url为:http://192.168.236.173:8020/order/findOrderByUserId/1?color=blueURI requestUrl = reconstructURI(serviceInstance, uri);if (log.isTraceEnabled()) {log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);}// 存入exchange对象中,之后netty发送请求会用到该urlexchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);exchange.getAttributes().put(GATEWAY_LOADBALANCER_RESPONSE_ATTR, response);supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, response));}).then(chain.filter(exchange)).doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(CompletionContext.Status.FAILED, throwable, lbRequest,exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR))))).doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(CompletionContext.Status.SUCCESS, lbRequest,exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR),new ResponseData(exchange.getResponse(), new RequestData(exchange.getRequest()))))));
}// 进入到choose()方法中
private Mono<Response<ServiceInstance>> choose(Request<RequestDataContext> lbRequest, String serviceId,Set<LoadBalancerLifecycle> supportedLifecycleProcessors) {ReactorLoadBalancer<ServiceInstance> loadBalancer = this.clientFactory.getInstance(serviceId,ReactorServiceInstanceLoadBalancer.class);if (loadBalancer == null) {throw new NotFoundException("No loadbalancer available for " + serviceId);}supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));// 调用ReactorLoadBalancer对象的choose()方法return loadBalancer.choose(lbRequest);
}



NettyRoutingFilter

发送netty请求

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 经过负载均衡之后的请求url,具体的下游服务请求地址// http://192.168.236.173:8020/order/findOrderByUserId/1?color=blueURI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);// scheme为httpString scheme = requestUrl.getScheme();if (isAlreadyRouted(exchange) || (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme))) {return chain.filter(exchange);}setAlreadyRouted(exchange);ServerHttpRequest request = exchange.getRequest();// GET 请求final HttpMethod method = HttpMethod.valueOf(request.getMethod().name());// url为 http://192.168.236.173:8020/order/findOrderByUserId/1?color=bluefinal String url = requestUrl.toASCIIString();// 请求头HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();filtered.forEach(httpHeaders::set);boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);// 路由对象Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange).headers(headers -> {headers.add(httpHeaders);// Will either be set below, or later by Nettyheaders.remove(HttpHeaders.HOST);if (preserveHost) {String host = request.getHeaders().getFirst(HttpHeaders.HOST);headers.add(HttpHeaders.HOST, host);}// 发送请求}).request(method).uri(url).send((req, nettyOutbound) -> {if (log.isTraceEnabled()) {nettyOutbound.withConnection(connection -> log.trace(...);}return nettyOutbound.send(request.getBody().map(this::getByteBuf));}).responseConnection((res, connection) -> {// Defer committing the response until all route filters have run// Put client response as ServerWebExchange attribute and write// response later NettyWriteResponseFilterexchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);ServerHttpResponse response = exchange.getResponse();// put headers and status so filters can modify the responseHttpHeaders headers = new HttpHeaders();res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);if (StringUtils.hasLength(contentTypeValue)) {exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, contentTypeValue);}setResponseStatus(res, response);// make sure headers filters run after setting status so it is// available in responseHttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(getHeadersFilters(), headers, exchange,Type.RESPONSE);if (!filteredResponseHeaders.containsKey(HttpHeaders.TRANSFER_ENCODING)&& filteredResponseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH)) {// It is not valid to have both the transfer-encoding header and// the content-length header.// Remove the transfer-encoding header in the response if the// content-length header is present.response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);}exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet());response.getHeaders().addAll(filteredResponseHeaders);return Mono.just(res);});Duration responseTimeout = getResponseTimeout(route);if (responseTimeout != null) {responseFlux = responseFlux.timeout(responseTimeout,Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout))).onErrorMap(TimeoutException.class,th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th));}return responseFlux.then(chain.filter(exchange));
}



补充知识

适配器模式

FilteringWebHandler#handle方法中,先获取路由的局部Filter,在创建一个集合存放全局Filter,在把局部Filter和全局Filter放在一起。这里就有一个问题:

局部Filter的类型是GatewayFilter,而全局Filter的类型是GlobalFilter,它们是怎么通过下面这种方式存放在一个集合中的嘞?

// 局部Filter
List<GatewayFilter> gatewayFilters = route.getFilters();
// 全局Filter
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
// 将局部Filter和全局Filter存入一个集合中
combined.addAll(gatewayFilters);



这里就用到了适配器模式,具体实现步骤是:

  • 创建一个GatewayFilterAdapter类,实现 GatewayFilter接口
  • GatewayFilterAdapter类中定义一个GlobalFilter属性,构造方法中传GlobalFilter类型的对象赋值给该属性
  • 实现GatewayFilter接口的filter()方法,在filter()方法中调用全局Filter的filter()方法
private static class GatewayFilterAdapter implements GatewayFilter {private final GlobalFilter delegate;GatewayFilterAdapter(GlobalFilter delegate) {this.delegate = delegate;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {return this.delegate.filter(exchange, chain);}
}



遍历List<GlobalFilter> globalFilters全局Filter,分别存入GatewayFilterAdapter对象中

List<GatewayFilterAdapter> ----> List<GatewayFilter> globalFilters集合

@Override
public Mono<Void> handle(ServerWebExchange exchange) {Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);// 局部FilterList<GatewayFilter> gatewayFilters = route.getFilters();// 全局Filter,这样就把全局GlobalFilter变为了GatewayFilter类型了List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);// 存入一个集合中combined.addAll(gatewayFilters);AnnotationAwareOrderComparator.sort(combined);// 调用各自的filter()方法return new DefaultGatewayFilterChain(combined).filter(exchange);
}



详细流程图

在这里插入图片描述

相关文章:

Gateway源码分析:路由Route、断言Predicate、Filter

文章目录 源码总流程图说明GateWayAutoConfigurationDispatcherHandlergetHandler()handleRequestWith()RouteToRequestUrlFilterReactiveLoadBalancerClientFilterNettyRoutingFilter 补充知识适配器模式 详细流程图 源码总流程图 在线总流程图 说明 Gateway的版本使用的是…...

ARM体系结构和接口技术(十)按键中断实验①

一、按键中断实验 &#xff08;一&#xff09;分析按键电路图 &#xff08;二&#xff09;芯片手册 二、按键中断实验分析 注&#xff1a;NVIC----Cortx-M核GIC----Cortx-A核 &#xff08;一&#xff09;查看所有外设的总线以及寄存器基地址 注&#xff1a;GIC的总线是A7核的…...

PostgreSQL使用(二)——插入、更新、删除数据

说明&#xff1a;本文介绍PostgreSQL的DML语言&#xff1b; 插入数据 -- 1.全字段插入&#xff0c;字段名可以省略 insert into tb_student values (1, 张三, 1990-01-01, 88.88);-- 2.部分字段插入&#xff0c;字段名必须写全 insert into tb_student (id, name) values (2,…...

有关css的题目

css样式来源有哪些&#xff1f; 内联样式&#xff1a; <a style"color: red"> </a> 内部样式&#xff1a;<style></style> 外部样式&#xff1a;写在独立的 .css文件中的 浏览器的默认样式 display有哪些属性 none - 不展示 block - 块类型…...

【开源库】libodb库编译及使用

前言 本文介绍windows平台下libodb库的编译及使用。 文末提供libodb-2.4.0编译好的msvc2019_64版本&#xff0c;可直接跳转自取 ODB库学习相关 【开源库学习】libodb库学习&#xff08;一&#xff09; 【开源库学习】libodb库学习&#xff08;二&#xff09; 【开源库学习】…...

电力需求预测挑战赛笔记 Task3 #Datawhale AI 夏令营

上文&#xff1a; 电力需求预测挑战赛笔记 Task2 #Datawhale AI 夏令营-CSDN博客文章浏览阅读80次。【代码】电力需求预测挑战赛笔记 Task2。https://blog.csdn.net/qq_23311271/article/details/140360632 前面我们介绍了如何使用经验模型以及常见的lightgbm决策树模型来解决…...

Promise 详解(原理篇)

目录 什么是 Promise 实现一个 Promise Promise 的声明 解决基本状态 添加 then 方法 解决异步实现 解决链式调用 完成 resolvePromise 函数 解决其他问题 添加 catch 方法 添加 finally 方法 添加 resolve、reject、race、all 等方法 如何验证我们的 Promise 是否…...

动态内存经典笔试题分析

目录 1.题目一 2.题目二 3.题目三 4.题目四 1.题目一 #include<stdlib.h> #include<stdio.h> #include<string.h> void GetMemory(char* p) {p (char*)malloc(100); } void Test(void) {char* str NULL;GetMemory(str);strcpy(str, "hello world…...

JS设计模式(一)单例模式

注释很详细&#xff0c;直接上代码 本文建立在已有JS面向对象基础的前提下&#xff0c;若无&#xff0c;请移步以下博客先行了解 JS面向对象&#xff08;一&#xff09;类与对象写法 特点和用途&#xff1a; 全局访问点&#xff1a;通过单例模式可以在整个应用程序中访问同一个…...

uniapp动态计算并设置元素高度

<template><view><scroll-view id"sv-box" :scroll-y"true" :style"{height:navHeightpx}"></scroll-view><view id"btn-box"><button>取消</button><button>确认</button><…...

直播架构如何设计核心节点和边缘节点

在直播架构中&#xff0c;核心节点和边缘节点的分工及主要服务是确保直播服务稳定、高效和可扩展的关键。以下是对这些节点的详细描述&#xff1a; 核心节点 核心节点通常位于数据中心&#xff0c;负责处理直播的主要逻辑和数据处理。其主要服务包括&#xff1a; 直播管理后…...

自动驾驶-预测概览

通过生成一条路径来预测一个物体的行为&#xff0c;在每一个时间段内&#xff0c;为每一辆汽车重新计算预测他们新生成的路径&#xff0c;这些预测路径为规划阶段做出决策提供了必要信息 预测路径有实时性的要求&#xff0c;预测模块能够学习新的行为。我们可以使用多源的数据…...

基于PSO算法优化PID参数的一些问题

目录 前言 Q1&#xff1a;惯性权重ω如何设置比较好&#xff1f;学习因子C1和C2如何设置&#xff1f; Q2&#xff1a;迭代速度边界设定一定能够遍历&#xff08;/覆盖&#xff09;整个PID参数二维空间范围吗&#xff1f;还是说需要与迭代次数相关&#xff1f;迭代次数越高&a…...

什么是决策树?

1. 什么是决策树&#xff1f; 决策树&#xff08;Decision Tree&#xff09;是一种常用的机器学习算法&#xff0c;用于解决分类和回归问题。它通过构建树结构来表示决策过程&#xff0c;分支节点表示特征选择&#xff0c;叶节点表示类别或回归值。 2. 决策树的组成部分 决策…...

ASP 快速参考

ASP 快速参考 概述 ASP&#xff08;Active Server Pages&#xff09;是一种由微软开发的服务器端脚本环境&#xff0c;用于动态网页设计和开发。它允许开发者创建和运行动态交互性网页&#xff0c;如访问数据库、发送电子邮件等。ASP页面通常以.asp为文件扩展名&#xff0c;并…...

(二)原生js案例之数码时钟计时

原生js实现的数字时间上下切换显示时间的效果&#xff0c;有参考相关设计&#xff0c;思路比较难&#xff0c;代码其实很简单 效果 代码实现 必要的样式 <style>* {padding: 0;margin: 0;}.content{/* text-align: center; */display: flex;align-items: center;justif…...

[CSS] 浮动布局的深入理解与应用

文章目录 浮动的简介元素浮动后的特点解决浮动产生的影响浮动后的影响解决浮动产生的影响 浮动相关属性实际应用示例示例1&#xff1a;图片与文字环绕示例2&#xff1a;多列布局示例3&#xff1a;响应式布局 总结 浮动布局是CSS中一种非常强大的布局方式&#xff0c;最初设计用…...

Linux云计算 |【第一阶段】ENGINEER-DAY2

主要内容&#xff1a; 磁盘空间管理fdisk、parted工具、开机自动挂载、文件系统、交换空间 KVM虚拟化 实操前骤&#xff1a; 1&#xff09;添加一块硬盘&#xff08;磁盘&#xff09;&#xff0c;需要关机才能进行操作&#xff0c;点击左下角【添加硬件】 2&#xff09;选择2…...

9.11和9.9哪个大?

没问题 文心一言 通义千问...

学懂C语言(十二):C语言中的二进制原理及应用

目录 1. 二进制原理 1.1 什么是二进制&#xff1f; 1.2 如何在C语言中表示二进制&#xff1f; 2. 二进制的表示 2.1 二进制和其他进制的转换 2.2 C语言中的二进制表示 3. 二进制运算 3.1 位运算符 3.2 计算过程示例 4. 应用示例 4.1 使用位运算实现开关 5. 总结 C语…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...