【源码】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/28 4:44:15 阅读更多 →
为什么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/27 21:50:41 阅读更多 →
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/26 23:10:29 阅读更多 →

最新新闻

在Oracle VM VirtualBox上 安装Ubuntu 16.04

在Oracle VM VirtualBox上 安装Ubuntu 16.04

首先 这是安装Ubuntu16.04需要的Ubuntu镜像源 链接:https://pan.baidu.com/s/1DjBwZO1qO4BSApJgSE0NEA 提取码:aho2 复制这段内容后打开百度网盘手机App,操作更方便哦 虚拟机软件可以上官网下载 https://www.virtualbox.org/wiki/Downloads …

2026/7/28 17:43:59 阅读更多 →
5大实战技巧:快速掌握Poppins字体的终极应用指南

5大实战技巧:快速掌握Poppins字体的终极应用指南

5大实战技巧:快速掌握Poppins字体的终极应用指南 【免费下载链接】Poppins Poppins, a Devanagari Latin family for Google Fonts. 项目地址: https://gitcode.com/gh_mirrors/po/Poppins 你是否正在寻找一款既能完美支持拉丁字母又能优雅呈现天城体文字的…

2026/7/28 17:43:59 阅读更多 →
企业级Agent智能体开发服务商私有化部署方案与落地场景解析

企业级Agent智能体开发服务商私有化部署方案与落地场景解析

我们公司决定上马AI智能体项目的时候,第一个拍板的原则就是:必须私有化部署。不为别的,我们手里握着大量客户的核心业务数据和内部经营数据,这些东西如果通过公有云API流转出去,万一出点什么事,谁也担不起这…

2026/7/28 17:43:59 阅读更多 →
2026主流品牌多层料箱批量读码盘点:固定式读码器如何择优?

2026主流品牌多层料箱批量读码盘点:固定式读码器如何择优?

前言 在制造业仓库与物流配送中心,料箱出入库、发料核对、托盘流转等环节长期依赖人工逐一扫码。操作员需从托盘上逐一取下料盘、扫描条码、再放回原处——面对整托数十甚至上百个料盘,这种逐一扫描模式耗时费力,同时因疲劳极易出现漏扫和重复…

2026/7/28 17:43:59 阅读更多 →
国内智能运维厂商哪家好?选型不再纠结:从“场景适配”看AIOps真实价值

国内智能运维厂商哪家好?选型不再纠结:从“场景适配”看AIOps真实价值

在运维领域,一个经常被讨论的问题是“国内智能运维厂商哪家好”。核心在于,企业的IT架构、数据成熟度、行业监管要求以及预算规模,共同决定了哪一类平台更适合。对于金融、能源、制造等行业的数字化转型来说,选型的本质&#xff0…

2026/7/28 17:43:59 阅读更多 →
TPIC7710EVM评估模块深度解析:汽车电子电机驱动与电流检测设计实战

TPIC7710EVM评估模块深度解析:汽车电子电机驱动与电流检测设计实战

1. 项目概述:TPIC7710EVM评估模块深度解析在汽车电子和嵌入式电机控制领域,拿到一颗功能强大的专用芯片(ASIC)后,如何快速、准确地验证其性能并搭建原型系统,是每个硬件工程师都会面临的挑战。德州仪器&…

2026/7/28 17:42:58 阅读更多 →

日新闻

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:43 阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:43 阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:43 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/28 12:04:22 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻