【源码】Spring Cloud Gateway 启动流程源码分析
以下分析以 spring-cloud-starter-gateway 4.1.0 源码为分析样本配置和启动类如果我们要使用 Spring Cloud Gateway需要在pom里引入如下依赖dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId version4.1.0/version /dependencyspring-cloud-starter-gateway里的依赖如下dependencies dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter/artifactId /dependency dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-gateway-server/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-loadbalancer/artifactId optionaltrue/optional /dependency /dependencies看上面引入了 spring-boot-starter-webflux为后面分析做铺垫除了引入依赖我们还需要有一个启动类如下import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; EnableDiscoveryClient Slf4j SpringBootApplication public class XXGatewayApp { public static void main(String[] args) { SpringApplication.run(XXGatewayApp.class, args); log.info(XX网关启动成功); } }启动 nettyserver在主类启动后是如何启动一个nettyserver的我们来分析一下跟踪启动类代码来到如下方法org.springframework.boot.SpringApplication#run(java.lang.String…)public ConfigurableApplicationContext run(String... args) { Startup startup SpringApplication.Startup.create(); if (this.registerShutdownHook) { shutdownHook.enableShutdownHookAddition(); } ... 省略代码... try { ApplicationArguments applicationArguments new DefaultApplicationArguments(args); ConfigurableEnvironment environment this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); Banner printedBanner this.printBanner(environment); // A1 创建 ConfigurableApplicationContext context this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // A2 触发创建server this.refreshContext(context); ... 省略代码... } catch (Throwable ex) { if (ex instanceof AbandonedRunException) { throw ex; } this.handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { if (context.isRunning()) { listeners.ready(context, startup.ready()); } return context; } catch (Throwable ex) { if (ex instanceof AbandonedRunException) { throw ex; } else { this.handleRunFailure(context, ex, (SpringApplicationRunListeners)null); throw new IllegalStateException(ex); } } }A1这个方法会执行以下逻辑org.springframework.boot.WebApplicationType#deduceFromClasspath计算web容器类型而最上面的pom依赖引入了 spring-boot-starter-webfluxstatic WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(org.springframework.web.reactive.DispatcherHandler, (ClassLoader)null) !ClassUtils.isPresent(org.springframework.web.servlet.DispatcherServlet, (ClassLoader)null) !ClassUtils.isPresent(org.glassfish.jersey.servlet.ServletContainer, (ClassLoader)null)) { return REACTIVE; } else { for(String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, (ClassLoader)null)) { return NONE; } } return SERVLET; } }上面代码根据类路径中加入的依赖返回REACTIVE最终返回 org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext 对象创建websever接上面 A2方法会触发AnnotationConfigReactiveWebServerApplicationContext如下调用注AnnotationConfigReactiveWebServerApplicationContext父类是ReactiveWebServerApplicationContext#createWebServerprivate void createWebServer() { WebServerManager serverManager this.serverManager; if (serverManager null) { StartupStep createWebServer this.getApplicationStartup().start(spring.boot.webserver.create); String webServerFactoryBeanName this.getWebServerFactoryBeanName(); //B1 创建ReactiveWebServerFactory 创建server的核心点 ReactiveWebServerFactory webServerFactory this.getWebServerFactory(webServerFactoryBeanName); createWebServer.tag(factory, webServerFactory.getClass().toString()); boolean lazyInit this.getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit(); //B2 WebServerManager构造方法创建server this.serverManager new WebServerManager(this, webServerFactory, this::getHttpHandler, lazyInit); this.getBeanFactory().registerSingleton(webServerGracefulShutdown, new WebServerGracefulShutdownLifecycle(this.serverManager.getWebServer())); //B3 很绝的方法 this.getBeanFactory().registerSingleton(webServerStartStop, new WebServerStartStopLifecycle(this.serverManager)); createWebServer.end(); } this.initPropertySources(); }B1 方法很绕会从ReactiveWebServerFactoryConfiguration里去获得NettyReactiveWebServerFactory的bean定义而这个bean定义依赖 ReactorResourceFactory 代码如下Bean NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory, ObjectProviderNettyRouteProvider routes, ObjectProviderNettyServerCustomizer serverCustomizers) { NettyReactiveWebServerFactory serverFactory new NettyReactiveWebServerFactory(); serverFactory.setResourceFactory(resourceFactory); Stream var10000 routes.orderedStream(); Objects.requireNonNull(serverFactory); var10000.forEach((xva$0) - serverFactory.addRouteProviders(new NettyRouteProvider[]{xva$0})); serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().toList()); return serverFactory; }也就是说在容器返回NettyReactiveWebServerFactory 对象前会把ReactorResourceFactory 对象初始化完毕ReactorResourceFactory 这个类实现了InitializingBean我们看看afterPropertiesSet方法初始化内容public void start() { synchronized(this.lifecycleMonitor) { if (!this.isRunning()) { if (!this.useGlobalResources) { if (this.loopResources null) { this.manageLoopResources true; this.loopResources (LoopResources)this.loopResourcesSupplier.get(); } if (this.connectionProvider null) { this.manageConnectionProvider true; this.connectionProvider (ConnectionProvider)this.connectionProviderSupplier.get(); } } else { Assert.isTrue(this.loopResources null this.connectionProvider null, useGlobalResources is mutually exclusive with explicitly configured resources); // C1 HttpResources httpResources HttpResources.get(); if (this.globalResourcesConsumer ! null) { this.globalResourcesConsumer.accept(httpResources); } this.connectionProvider httpResources; this.loopResources httpResources; } this.running true; } } }C1方法最终执行的是 reactor.netty.resources.LoopResources#create(java.lang.String)static LoopResources create(String prefix) { if (((String)Objects.requireNonNull(prefix, prefix)).isEmpty()) { throw new IllegalArgumentException(Cannot use empty prefix); } else { return new DefaultLoopResources(prefix, DEFAULT_IO_SELECT_COUNT, DEFAULT_IO_WORKER_COUNT, true); } }是不是很熟悉了是创建的reactor.netty.resources.DefaultLoopResources 对象todo~~B2 方法会调用到如下方法org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory#getWebServerpublic WebServer getWebServer(HttpHandler httpHandler) { // D1 创建 HttpServer HttpServer httpServer this.createHttpServer(); ReactorHttpHandlerAdapter handlerAdapter new ReactorHttpHandlerAdapter(httpHandler); NettyWebServer webServer this.createNettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout, this.getShutdown()); webServer.setRouteProviders(this.routeProviders); return webServer; }D1 方法会执行到如下方法 org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory#createHttpServerprivate HttpServer createHttpServer() { HttpServer server HttpServer.create().bindAddress(this::getListenAddress); if (Ssl.isEnabled(this.getSsl())) { server this.customizeSslConfiguration(server); } if (this.getCompression() ! null this.getCompression().getEnabled()) { CompressionCustomizer compressionCustomizer new CompressionCustomizer(this.getCompression()); server compressionCustomizer.apply(server); } server server.protocol(this.listProtocols()).forwarded(this.useForwardHeaders); return this.applyCustomizers(server); }继续分析上面B3这个地方太绝了向容器中注入了一个 WebServerStartStopLifecycle这种类型的类会被框架中触发start方法触发一些列的start方法最终执行的是 reactor.netty.http.server.HttpServerBind父类 ---- HttpServer 父类 ----- reactor.netty.transport.ServerTransport的 bindNow方法 ---- bind方法。重要方法 reactor.netty.transport.ServerTransport#bindpublic Mono? extends DisposableServer bind() { CONF config (CONF)(this.configuration()); Objects.requireNonNull(config.bindAddress(), bindAddress); Mono? extends DisposableServer mono Mono.create((sink) - { SocketAddress local (SocketAddress)Objects.requireNonNull((SocketAddress)config.bindAddress().get(), Bind Address supplier returned null); if (local instanceof InetSocketAddress) { InetSocketAddress localInet (InetSocketAddress)local; if (localInet.isUnresolved()) { local AddressUtils.createResolved(localInet.getHostName(), localInet.getPort()); } } boolean isDomainSocket false; DisposableBind disposableServer; if (local instanceof DomainSocketAddress) { isDomainSocket true; disposableServer new UdsDisposableBind(sink, config, local); } else { disposableServer new InetDisposableBind(sink, config, local); } ConnectionObserver childObs new ChildObserver(config.defaultChildObserver().then(config.childObserver())); // E1 Acceptor acceptor new Acceptor(config.childEventLoopGroup(), config.channelInitializer(childObs, (SocketAddress)null, true), config.childOptions, config.childAttrs, isDomainSocket); // E2 TransportConnector.bind(config, new AcceptorInitializer(acceptor), local, isDomainSocket).subscribe(disposableServer); }); if (config.doOnBind() ! null) { mono mono.doOnSubscribe((s) - config.doOnBind().accept(config)); } return mono; }E1最终调用 reactor.netty.resources.DefaultLoopResources#cacheNioServerLoops 获得work线程池E2最终调用 reactor.netty.resources.DefaultLoopResources#cacheNioSelectLoops 获得boss线程池问题来了看reactor.netty.resources.LoopResources源码如果系统参数里没有配置reactor.netty.ioSelectCount则boss线程会和work线程池的AtomicReference在cacheNioSelectLoops 方法中返回同一对象这是不是会造成线程池共用可以参看 https://blog.csdn.net/qq_42651904/article/details/134561804 这篇文章理解那么你们生产环境会单独设置 reactor.netty.ioSelectCount 参数吗jvisualvm监控验证如果不设置reactor.netty.ioSelectCount 这个启动参数通过监控网关启动后线程名称是 reactor-http-nio-xx如果在启动的时候设置这个参数为1启动的线程不一样 reactor-http-select-xxpublic static void main(String[] args) { System.setProperty(reactor.netty.ioSelectCount, 1); SpringApplication.run(XXXGatewayApp.class, args); log.info(XXX网关启动成功); }设想一下如果代码写得不好在GlobalFilter有阻塞写法比如数据查询、redis查询加上高并发请求那么是不是会影响boss线程“接客”后面再通过压测的方式论证一下。

相关新闻

番茄小说下载器:10分钟搭建你的永久离线小说库

番茄小说下载器:10分钟搭建你的永久离线小说库

番茄小说下载器:10分钟搭建你的永久离线小说库 【免费下载链接】fanqienovel-downloader 下载番茄小说 项目地址: https://gitcode.com/gh_mirrors/fa/fanqienovel-downloader 你是否经历过在地铁上网络信号差无法追更心爱小说的烦恼?是否担心某天…

2026/7/22 19:23:50 阅读更多 →
为什么uber-apk-signer成为Android开发者的首选签名工具:技术原理与实践指南

为什么uber-apk-signer成为Android开发者的首选签名工具:技术原理与实践指南

为什么uber-apk-signer成为Android开发者的首选签名工具:技术原理与实践指南 【免费下载链接】uber-apk-signer A cli tool that helps signing and zip aligning single or multiple Android application packages (APKs) with either debug or provided release c…

2026/7/23 0:52:18 阅读更多 →
STDF Viewer终极指南:如何5分钟完成半导体测试数据分析

STDF Viewer终极指南:如何5分钟完成半导体测试数据分析

STDF Viewer终极指南:如何5分钟完成半导体测试数据分析 【免费下载链接】STDF-Viewer A free GUI tool to visualize STDF (semiconductor Standard Test Data Format) data files. 项目地址: https://gitcode.com/gh_mirrors/st/STDF-Viewer STDF Viewer是一…

2026/7/21 13:10:42 阅读更多 →

最新新闻

TensorFlow入门指南:从安装到第一个神经网络模型

TensorFlow入门指南:从安装到第一个神经网络模型

1. TensorFlow入门指南:从安装到第一个模型 TensorFlow作为当前最流行的机器学习框架之一,已经成为了AI开发者的标配工具。我第一次接触TensorFlow是在2016年,当时还在用1.x版本,如今已经发展到2.x系列,API变得更加友好…

2026/7/23 12:25:03 阅读更多 →
ABTS自由基清除能力检测:评估天然产物抗氧化活性的经典比色工具

ABTS自由基清除能力检测:评估天然产物抗氧化活性的经典比色工具

ABTS自由基清除能力检测试剂盒在抗氧化研究与功能性食品评价中的广泛应用在生物体内,活性氧(Reactive Oxygen Species, ROS)是细胞代谢过程中不可避免产生的副产物。适度的ROS参与细胞信号转导和免疫防御,但当ROS产生与清除之间的…

2026/7/23 12:25:03 阅读更多 →
《从0到1搭建私域社群SOP手册》免费领:一个人也能搭出高转化社群

《从0到1搭建私域社群SOP手册》免费领:一个人也能搭出高转化社群

你是否也在面临这些困境:社群发广告、甩优惠券,用户领完福利就退群;活动没人互动,社群逐渐沦为“死群”。为此,大圆系统性地梳理了社群运营方法论,推出这份《从0到1搭建私域社群SOP手册》。各行业都可以按照…

2026/7/23 12:25:03 阅读更多 →
fastapi 小demo入门

fastapi 小demo入门

首先安装,打开终端:pip install fastapi pip install fastapi[standard]写测试代码:from fastapi import FastAPIapp FastAPI() app.get("/") def root():return {"Hello": "World"}app.get("/items&quo…

2026/7/23 12:25:03 阅读更多 →
制造业ERP上线前要准备哪些基础数据?物料、BOM、工艺与库存检查清单

制造业ERP上线前要准备哪些基础数据?物料、BOM、工艺与库存检查清单

很多中小制造企业第一次上线ERP时,往往把主要精力放在功能选择和系统配置上。真正上线后才发现,MRP计划无法执行、库存账实不符、领料发料频繁出错,很多问题并不是系统功能不足,而是基础数据没有提前整理清楚。基础数据是ERP运行的…

2026/7/23 12:25:03 阅读更多 →
从基料到配比:武汉知医邦中药膨化产品开发的核心配料逻辑

从基料到配比:武汉知医邦中药膨化产品开发的核心配料逻辑

一、引言"药食同源" 是中医药理论的重要组成部分,指许多食物同时兼具药用价值,既能满足日常饮食需求,又能调理身体、预防疾病。随着大健康产业的快速发展,如何将传统中药材与现代食品加工技术相结合,解决中药…

2026/7/23 12:24:03 阅读更多 →

日新闻

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击: https://intelliparadigm.com 第一章:从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表) 当AI副业主理人不再仅满足于单次服务交付,而是主动构建可复用、可裂变、可…

2026/7/23 0:00:25 阅读更多 →
AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

更多请点击: https://codechina.net 第一章:AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析 在对2,346篇跨行业AI生成文案的A/B测试数据进行聚类分析后,我们发现&#xff1…

2026/7/23 0:01:26 阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:01:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 12:54:44 阅读更多 →

月新闻