Spring Cloud Gateway 请求转发源码分析

一、背景

Spring Cloud Gateway 作为一种微服务网关组件,相信大家都不陌生,一个请求经过Spring Cloud Gateway是如何转发出去的,今天我们就来分析一下这部分的源码。

二、正文

下面这张图大家在学习Spring Cloud Gateway的时候肯定见过,在分析源码之前我们再来看下这张图。 

 其中

  • Gateway Client: 发送请求到 Spring Cloud Gateway 的客户端
  • Gateway Handler Mapping: 是处理请求的组件,负责将请求映射到相应的处理器。处理请求将被路由到哪个路由规则,从而选择对应的过滤器链
  • Gateway Web Handler: 实际处理请求的组件,会依次执行过滤器链,对请求进行处理
  • Gateway Filter: 过滤器链由多个过滤器组成,每个过滤器执行一些特定代码逻辑
  • Proxied Service: 被代理的服务,当执行完过滤器链之后会将请求转发到具体的目标服务

官网上有这样一段解释:

📌Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a route, it is sent to the Gateway Web Handler. This handler runs the request through a filter chain that is specific to the request。

客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 确定请求与路由匹配,则会将其发送到 Gateway Web Handler。 此 handler 通过一个特定于请求的过滤器链来运行请求(这个处理程序会让请求通过一系列只为这个请求设计的过滤器。简单来说,就是请求会经过一组专门为它设置的过滤器。

我们先来看下 gateway 的自动配置类 GatewayAutoConfiguration

Gateway的自动配置

。整理了一份好像面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

less

代码解读

复制代码

@Configuration(    proxyBeanMethods = false ) @ConditionalOnProperty(    name = {"spring.cloud.gateway.enabled"},    matchIfMissing = true ) @EnableConfigurationProperties //自动配置前置条件:引入了WebFlux 和 HttpHandler 组件 @AutoConfigureBefore({HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class}) //自动配置后置组件:负载均衡组件 @AutoConfigureAfter({GatewayReactiveLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class}) @ConditionalOnClass({DispatcherHandler.class}) public class GatewayAutoConfiguration {        @Bean    @ConditionalOnMissingBean    public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {        return new PropertiesRouteDefinitionLocator(properties);   }        @Bean    @Primary    public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {        return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));   }        @Bean    @ConditionalOnMissingBean    public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {        return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);   }        @Bean    public GatewayProperties gatewayProperties() {        return new GatewayProperties();   }        @Bean    @ConditionalOnEnabledGlobalFilter    public RouteToRequestUrlFilter routeToRequestUrlFilter() {        return new RouteToRequestUrlFilter();   }        /**     * GlobalFilter自动注入使用的是 @Autowired,不需要显式写出,spring 会自动查找所有类型为 GlobalFilter 的 Bean,并将它们注入到这个List中     */    @Bean    public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {        return new FilteringWebHandler(globalFilters);   }       ......     }

自动配置类GatewayAutoConfiguration在内部初始化了很多bean,列举几个重要的如下:

  • PropertiesRouteDefinitionLocator:用于从配置文件(yml/properties)中读取路由配置信息
  • RouteDefinitionLocator:把 RouteDefinition 转化为 Route
  • RoutePredicateHandlerMapping:类似于 mvc 的HandlerMapping,用于匹配对应的请求 Route
  • GatewayProperties:yml配置信息封装在 GatewayProperties 对象中
  • GlobalFilter实现类:比如RouteToRequestUrlFilter、ForwardRoutingFilter等全局过滤器
  • FilteringWebHandler:注入了所有实现了 GlobalFilter 接口的过滤器类

FilteringWebHandler 中注入的GlobalFilter如下:

 

perl

代码解读

复制代码

{  "org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@77856cc5": 10100,  "org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@4f6fd101": 10000,  "org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@32d22650": -1,  "org.springframework.cloud.gateway.filter.ForwardRoutingFilter@106459d9": 2147483647,  "org.springframework.cloud.gateway.filter.NettyRoutingFilter@1fbd5e0": 2147483647,  "org.springframework.cloud.gateway.filter.ForwardPathFilter@33a71d23": 0,  "org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@135064ea": 2147483637,  "org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@23c05889": 2147483646 }

Gateway的工作流程

  1. 所有请求都将由ReactorHttpHandlerAdapter.apply() 方法拦截处理,此时会封装请求对象和响应对象,并传递到HttpWebHandlerAdapter.handle()方法。

  2. HttpWebHandlerAdapter.handle()方法将request和response封装成上下文对象ServerWebExchange,方法通过getDelegate()获取全局异常处理器ExceptionHandlingWebHandler执行全局异常处理

    这两步出现的类是在 spring-web 包下

  3. ExceptionHandlingWebHandler执行完成后,调用DispatcherHandler.handle(),循环所有handlerMappings查找处理当前请求的HandlerDispatcherHandler

    在 spring-webflux 包下,我们知道Gateway是基于webflux响应式编程

  4. 找到Handler后调用DispatcherHandler.invokeHandler()执行找到的Handler,此时会调用FilteringWebHandler.handle()

  5. DefaultGatewayFilterChain.filter()是关键流程,所有过滤器都会在这里执行,比如服务查找、负载均衡、远程调用等,都在这一块。

Gateway源码分析

📌Spring Cloud Gateway版本:spring-cloud-gateway 3.1.4

我们直接从HttpWebHandlerAdapter.handle()看起

HttpWebHandlerAdapter
 

kotlin

代码解读

复制代码

public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {        if (this.forwardedHeaderTransformer != null) {            try {                request = this.forwardedHeaderTransformer.apply(request);           } catch (Throwable var4) {                Throwable ex = var4;                if (logger.isDebugEnabled()) {                    logger.debug("Failed to apply forwarded headers to " + this.formatRequest(request), ex);               } ​                response.setStatusCode(HttpStatus.BAD_REQUEST);                return response.setComplete();           }       }        // 创建网关上下文对象        ServerWebExchange exchange = this.createExchange(request, response);        LogFormatUtils.traceDebug(logger, (traceOn) -> {            return exchange.getLogPrefix() + this.formatRequest(exchange.getRequest()) + (traceOn ? ", headers=" + this.formatHeaders(exchange.getRequest().getHeaders()) : "");       });        // getDelegate()获取当前的Handler        Mono var10000 = this.getDelegate().handle(exchange).doOnSuccess((aVoid) -> {            this.logResponse(exchange);       }).onErrorResume((exx) -> {            return this.handleUnresolvedError(exchange, exx);       });        response.getClass();        return var10000.then(Mono.defer(response::setComplete));   }

代码中先创建了网关上下文对象ServerWebExchange,然后getDelegate()获取当前的Handler,我们将断点打到getDelegate() 方法里面:

当前返回的 WebHandler 是ExceptionHandlingWebHandler,而ExceptionHandlingWebHandler的 delegate 是FilteringWebHandlerFilteringWebHandler的 delegate 是DispatcherHandler,所有的 delegate 的handle()方法都会依次执行。

delegate: 委托、委派 这里应该是使用了委派模式,不属于23种设计模式,类似代理模式,委派模式主要关注任务的调度和分配,注重结果,可以看作是一种特殊的静态代理,即全权代理。

接下来我们可以把断点放到DispatcherHandler.handler()方法上

DispatcherHandler
 

kotlin

代码解读

复制代码

 public Mono<Void> handle(ServerWebExchange exchange) {        if (this.handlerMappings == null) {            return this.createNotFoundError();       } else {            // 1.遍历所有的handlerMapping,获取到当前网关上下文中的多个handlerMapping            return CorsUtils.isPreFlightRequest(exchange.getRequest()) ? this.handlePreFlight(exchange) : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {                return mapping.getHandler(exchange);            // 2.获取对应的适配器,调用对应的处理器           }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {                return this.invokeHandler(exchange, handler);            // 3. 返回处理结果           }).flatMap((result) -> {                return this.handleResult(exchange, result);           });       }   }

 可以看到这里所有的handlerMapping ,方法中会调用所有 handlerMappings 的getHandler(exchange)方法,

点进去getHandler(exchange)方法进入一个抽象类AbstractHandlerMapping

AbstractHandlerMapping
 

kotlin

代码解读

复制代码

public Mono<Object> getHandler(ServerWebExchange exchange) {        return this.getHandlerInternal(exchange).map((handler) -> {            if (this.logger.isDebugEnabled()) {                this.logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);           } ​            ServerHttpRequest request = exchange.getRequest();            if (this.hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {                CorsConfiguration config = this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null;                CorsConfiguration handlerConfig = this.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;               }           } ​            return handler;       });   }

类中的getHandler(exchange)方法会调getHandlerInternal(exchange)方法 

image.png

 可以看到getHandlerInternal(exchange)该方法由各个HandlerMapping自行实现,这里有6个重写了getHandlerInternal方法的类,

由于是网关组件,当请求进入时,会先判断路由,所以会进入实现类RoutePredicateHandlerMapping中。

RoutePredicateHandlerMapping
 

kotlin

代码解读

复制代码

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());            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);                // 此处的webhandler是FilteringWebhandler                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) + "]");               } ​           })));       }   }   ​ 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;       });   } ​

lookupRoute中的this.routeLocator.getRoutes()会通过RouteDefinitionRouteLocator拿到yml配置文件中所有的路由断言工厂(Before、After、Path等等),然后把找到的路由转换成Route,再执行apply方法,进行路由匹配,判断是否允许请求通过。

找到对应Route后会返回指定的FilterWebHandler,如下代码: 

image.png

 至此对应着DispatcherHandler中的第一步获取到当前网关上下文中的多个handlerMapping.

接下来我们看DispatcherHandler中的第二步获取对应的适配器,调用对应的处理器.

进入获取对应的适配器方法 invokeHandler(exchange, handler)中 

image.png

image.png

 

 请求对应的适配器是SimpleHandlerAdapter

SimpleHandlerAdapter
 

typescript

代码解读

复制代码

public class SimpleHandlerAdapter implements HandlerAdapter {    public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {        WebHandler webHandler = (WebHandler)handler;        Mono<Void> mono = webHandler.handle(exchange);        return mono.then(Mono.empty());   } }

image.png

这里调用了FilteringWebHandlerhandle()方法

FilteringWebHandler
 

ini

代码解读

复制代码

   public Mono<Void> handle(ServerWebExchange exchange) {        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);       } ​        return (new DefaultGatewayFilterChain(combined)).filter(exchange);   }

image.png

这里面组装了所有使用到的过滤器,使用责任链的设计模式去实现调用,过滤器按照一定顺序排序,order值越小越先执行,

我们看RouteToRequestUrlFilterForwardRoutingFilter过滤器.

RouteToRequestUrlFilter:路由转换路由转换,把http://localhost:8888/order/test0 —> lb://mall-order/order/test0

ForwardRoutingFilter:转发路由网关过滤器。其根据 forward:// 前缀( Scheme )过滤处理,将请求转发到当前网关实例本地接口。

RouteToRequestUrlFilter
 

scss

代码解读

复制代码

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        // 从exchange中取路由Route对象        Route route = (Route)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);        if (route == null) {            return chain.filter(exchange);       } else {            log.trace("RouteToRequestUrlFilter start");            // 取当前请求uri:http://localhost:8888/order/test0            URI uri = exchange.getRequest().getURI();            boolean encoded = ServerWebExchangeUtils.containsEncodedParts(uri);            // 路由对象中保存的uri,也就是我们在yml文件中配置的值:   lb://mall-order            URI routeUri = route.getUri();            if (hasAnotherScheme(routeUri)) {                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());                routeUri = URI.create(routeUri.getSchemeSpecificPart());           } ​            if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {                throw new IllegalStateException("Invalid host: " + routeUri.toString());           } else {                // 转换结果为:lb://mall-order/order/test0                URI mergedUrl = UriComponentsBuilder.fromUri(uri).scheme(routeUri.getScheme()).host(routeUri.getHost()).port(routeUri.getPort()).build(encoded).toUri();                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, mergedUrl);                return chain.filter(exchange);           }       }   }

再往后就是通过NettyRoutingFilter发起远程调用

NettyRoutingFilter

SpringCloud在实现对后端服务远程调用是基于Netty发送Http请求实现,核心代码在NettyRoutingFilter.filter()中,其中核心代码为send()方法

 

ini

代码解读

复制代码

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);        String scheme = requestUrl.getScheme();        if (!ServerWebExchangeUtils.isAlreadyRouted(exchange) && ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme))) {            ServerWebExchangeUtils.setAlreadyRouted(exchange);            ServerHttpRequest request = exchange.getRequest();            HttpMethod method = HttpMethod.valueOf(request.getMethodValue());            String url = requestUrl.toASCIIString();            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);            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().addAll(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);       }   } ​

上面send方法最终会调用ChannelOperations#send()方法,而该方法其实是基于了Netty实现数据发送。

至此,一次Spring Cloud Gateway请求转发源码分析结束。

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud Gateway 是一个基于 Spring Framework 5、Spring Boot 2 和 Project Reactor 的 API 网关服务。它提供了一种简单而强大的方法来路由和过滤请求,并将它们转发到底层的微服务。对于理解 Spring Cloud Gateway 的工作原理和深入研究其源代码是非常有用的。 首先,通过源码可以发现 Spring Cloud Gateway 主要由三个核心模块组成:路由模块、过滤器模块和事件模块。路由模块负责根据定义的路由规则将请求转发到特定的目标地址。过滤器模块负责在请求的不同阶段执行一系列的过滤器来处理请求。事件模块则用于处理与路由和过滤器相关的异步事件。 源码中的路由模块使用了 Reactive Streams API 中的 Flux 和 Mono 类来处理异步操作。它利用 RouterFunction 和 HandlerFunction 来定义路由和处理请求的方法,并通过 RoutePredicateFactory 来解析和匹配路由规则。在路由模块中,使用了 Netty 库来实现底层的网络通信和请求转发。 通过源码分析过滤器模块,可以发现 Spring Cloud Gateway 的过滤器分为全局过滤器和自定义过滤器两种类型。全局过滤器在请求的全局范围内应用,并且可以用于添加一些全局的处理逻辑。自定义过滤器则允许开发者根据需要添加自定义的过滤逻辑。过滤器的执行顺序可以通过 Order 注解来控制,以满足不同过滤器的执行顺序需求。 事件模块在源码中使用了 Reactor 提供的 EventProcessor 来处理与路由和过滤器相关的事件。它使用了 Reactor 的 FluxSink 和 MonoSink 来创建异步事件源,并通过事件处理器将事件发送给注册的监听器。通过查看事件模块的源码,可以更加深入地了解 Spring Cloud Gateway 是如何处理与路由和过滤器相关的事件的。 总结而言,通过源码分析 Spring Cloud Gateway,我们可以更好地了解其内部的工作原理和实现细节。这对于开发者来说是非常有用的,因为它可以帮助我们更好地使用和扩展 Spring Cloud Gateway 来满足不同的场景需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值