
以下分析以 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线程“接客”后面再通过压测的方式论证一下。