两段式轻松梳理SpringCloudGateway源码核心脉络

2,229 阅读6分钟

如果你看了这个标题就点进来了,那么恭喜我吧(是越来越会起标题啦),上一篇《把SpringCloudGateway这些东西理一理,感觉我又飞升了》已经完成两个多月了,里面主要介绍了一些基础概念,调用流程,重点分析了Route路由,但从源码层面对整个调用流程还没有一个完整的分析,心里总觉得缺点啥,准备在春节前写一篇,继续飞升一波,也算作为一个告别旧年的收官之作。

u=2739486378,152672279&fm=30&app=106&f=PNG.png

那这篇文章重点讲述两个事情,通过这两大块,我们就可以对SCG核心源码有一个比较清晰的脉络:

  1. 服务启动阶段在搞啥?
  2. 接口调用阶段在搞啥?

梳理了一个核心总体关注点,可以先瞄一眼,完事后再回头来看看这个图:

执行阶段 (1).png

1. 服务启动阶段

SCG基于Spring-Boot自动装配,装载流程大家懂的都懂,我们就直接可以看spring-cloud-gateway-server-xx.jarMETA-INF下的spring.factories配置文件, 下面是该配置文件下关于自动装配的定义,根据我理解的重要程度排了个序,后面时间富余情况下再展开梳理梳理(心急的朋友可以先看看这篇todo):

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
# 校验依赖包(1星)
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\
# 核心自动配置(5星)
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
# 断路器配置(3星)
org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration,\
# 无负载配置(2星)
org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration,\
# 监控指标配置(2星)
org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,\
# 流控配置(3星)
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,\
# 网关发现(4星)
org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration,\
# 跨域配置(2星)
org.springframework.cloud.gateway.config.SimpleUrlHandlerMappingGlobalCorsAutoConfiguration,\
# 负载配置(3星)
org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration,\
# oauth2客户端配置(2星)
org.springframework.cloud.gateway.config.GatewayReactiveOAuth2AutoConfiguration

那么,接下来我们就主要跟进一下在注入到Spring容器过程中重点做了些啥事,又主要分为两阶段:

首先,我们考虑SCG的响应式服务是从哪开始的,如何识别到和普通的应用使用不同的Web容器呢?Spring目前对应支持的容器有如下这些:

在实例化SpringApplication过程中,会去设置一个webApplicationType字段,该字段用于后期实例化不同的Web容器,

image.png

那我们的疑问来了,这个类是怎么引入的呢? 在我们做实际项目的网关服务,SCG对应使用底层是使用Netty作为Web服务的,这使得网关能够支撑更多流量请求,在网关项目中引入spring-boot-starter-webflux依赖后,有了它之后,应用服务将默认使用Netty作为服务容器,

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

这个依赖主要引入了spring-webflux包,里面就定义了:org.springframework.web.reactive.DispatcherHandler,有了这个类,应用服务后器才能启动一个Netty响应式容器。

Spring在refresh过程中有一个重要方法 onRefresh(),这个方法里面就重点来用于实例化不同的Web容器。我们现在使用的是ReactiveWebServerApplicationContext,其它传统的ApplicationContext流程类似。

image.png

createWebServer方法中最核心的方法,内部又进一步根据当前Web环境的中容器类型来实例化不同的Web容器,当前响应式默认主要支持:Netty、Jetty、Tomcat和Undertow。

private void createWebServer() {
        ...
        this.serverManager = new WebServerManager(this, webServerFactory, this::getHttpHandler, lazyInit);
        ...
}

根据配置,服务是要实例化Netty服务,NettyWebServer提供了一个重要的钩子start()方法(Spring refresh的finishRefresh进行触发的),这个方法作用又继续调用startHttpServer(),一个就是将handler提交给Netty服务用于Http请求的回调,再一个就是把服务启起来。

DisposableServer startHttpServer() {
    HttpServer server = this.httpServer;
    if (this.routeProviders.isEmpty()) {
        server = server.handle(this.handler);
    } else {
        server = server.route(this::applyRouteProviders);
    }

    return this.lifecycleTimeout != null ? server.bindNow(this.lifecycleTimeout) : server.bindNow();
}

这点,大家又比较好奇handler是什么了,具体看下一节:接口调用阶段。

2. 接口调用阶段

书接前节,当发起Rest或WebSocket等调用时,请求至Netty服务器,handler即被回调来处理该请求,它是定义为ReactorHttpHandlerAdapter,本身实现了BiFunction,在其内部持有一个HttpHandler实例,在handler的apply方法中就重点调用HttpHandler实例的handle方法,进一步,在HttpHandler一个重要的实现类HttpWebHandlerAdapter中,内部又持有一个WebHandler实例对象。

public interface HttpHandler {
    Mono<Void> handle(ServerHttpRequest var1, ServerHttpResponse var2);
}

public interface WebHandler {
    Mono<Void> handle(ServerWebExchange var1);
}

没缓过来的朋友回头看看流程图走哪儿来了,只要主线还在我们就不慌,看源码就是这样:细枝末节先忽略,先把握主线!!核心:HttpHandler的核心方法handle,其首先通过request和response得到了一个exchange,然后调用WebHandler来执行我们想要做的处理。

Ok,继续,上面的HttpHandler实例的handle方法会调用org.springframework.web.server.handler.ExceptionHandlingWebHandler,它的核心作用就是调用同包下的FilteringWebHandler 触发过滤器链调用,以及处理各种异常Handler拦截,比如我们通常会在网关自定义全局 WebExceptionHandler用于统一异常处理,都是在这点执行的。

FilteringWebHandler定义如下:

image.png

继续Debug调试,终于可以在DispatcherHandler中可以看见和具体路由相关的处理了,首先,我们在配置文件或者nacos等这样的注册中心注册了格式各样的Router,在这就会根据exchange中的Url来获取到具体的FilteringWebHandler对象,然后进一步适配是哪一种请求方式,来确定具体的过滤逻辑。 20230116163409.png

在invokerHandler中具体有4种Http请求适配定义,4种中优先级最低的匹配方式:

HttpAdapter类型优先级说明
WebSocketHandlerAdapter1WebSocket请求
RequestMappingHandlerAdapter2用于处理使用@RequestMapping注解修饰的方法,(当前项目的中的Rest接口)。
HandlerFunctionAdapter3用于处理实现HandlerFunction接口的实现类,调用其handle方法处理请求
SimpleHandlerAdapter4匹配WebHandler即可

网关调用是基于这个匹配的SimpleHandlerAdapter,接下来就是执行和业务最最紧密相关的逻辑调用了,即位于org.springframework.cloud.gateway.handler下的FilteringWebHandler的handle方法,这个也是在上一篇中提到过的路由的各种过滤器优先级执行的核心处理逻辑:

image.png FilteringWebHandler执行流程说明:

  1. 构建一个包含全局过滤器的集合(combined);
  2. 获取上下中的路由信息GATEWAY_ROUTE_ATTR ;
  3. 将路由里的过滤器添加到集合中(combined);
  4. 对过滤器集合进行排序操作;
  5. 通过过滤器集合组装过滤器链表,并进行调用(DefaultGatewayFilterChain与Servlet中的FilterChain与原理是一致的);
  6. 通过过滤器来处理请求到具体业务服务。

好了,SCG网关调用核心脉络算是梳理完了,如果大家有什么疑问或者文章有什么不正确的,有兴趣的小伙伴可以一起再交流交流。

image.png