最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Cloud Gateway 啟動流程源碼分析

 更新時間:2026年01月13日 09:33:19   作者:萬物皆字節(jié)  
文章詳細分析了Spring Cloud Gateway 4.1.0啟動過程,從依賴引入、啟動類配置到NettyServer的啟動,解析了關(guān)鍵方法和類的調(diào)用鏈,并探討了線程池的配置和潛在風險,感興趣的朋友跟隨小編一起看看吧

以下分析以 spring-cloud-starter-gateway 4.1.0 源碼為分析樣本。

配置和啟動類

如果我們要使用 Spring Cloud Gateway,需要在pom里引入如下依賴:

<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-gateway</artifactId>
			<version>4.1.0</version>
		</dependency>

spring-cloud-starter-gateway里的依賴如下:

<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-gateway-server</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-loadbalancer</artifactId>
			<optional>true</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網(wǎng)關(guān)啟動成功!");
    }
}

啟動 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 創(chuàng)建 ConfigurableApplicationContext
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            // @A2 觸發(fā)創(chuàng)建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:這個方法會執(zhí)行以下邏輯
org.springframework.boot.WebApplicationType#deduceFromClasspath
計算web容器類型,而最上面的pom依賴引入了 spring-boot-starter-webflux

    static 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;
        }
    }

上面代碼根據(jù)類路徑中加入的依賴,返回REACTIVE,最終返回 org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext 對象

創(chuàng)建websever

接上面 @A2方法,會觸發(fā)AnnotationConfigReactiveWebServerApplicationContext如下調(diào)用:

注:AnnotationConfigReactiveWebServerApplicationContext父類是
ReactiveWebServerApplicationContext#createWebServer

    private void createWebServer() {
        WebServerManager serverManager = this.serverManager;
        if (serverManager == null) {
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            String webServerFactoryBeanName = this.getWebServerFactoryBeanName();
//@B1 創(chuàng)建ReactiveWebServerFactory ,創(chuàng)建server的核心點
            ReactiveWebServerFactory webServerFactory = this.getWebServerFactory(webServerFactoryBeanName);
            createWebServer.tag("factory", webServerFactory.getClass().toString());
            boolean lazyInit = this.getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit();
//@B2 WebServerManager構(gòu)造方法創(chuàng)建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, ObjectProvider<NettyRouteProvider> routes, ObjectProvider<NettyServerCustomizer> 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 這個類實現(xiàn)了InitializingBean,我們看看afterPropertiesSet方法初始化內(nèi)容:

    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方法最終執(zhí)行的是 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);
        }
    }

是不是很熟悉了,是創(chuàng)建的reactor.netty.resources.DefaultLoopResources 對象

todo~~

@B2 方法會調(diào)用到如下方法:
org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory#getWebServer

    public WebServer getWebServer(HttpHandler httpHandler) {
// @D1 創(chuàng)建 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 方法會執(zhí)行到如下方法: org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory#createHttpServer

    private 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);
    }

繼續(xù)分析上面:@B3
這個地方太絕了,向容器中注入了一個 WebServerStartStopLifecycle,這種類型的類會被框架中觸發(fā)start方法,觸發(fā)一些列的start方法,最終執(zhí)行的是 reactor.netty.http.server.HttpServerBind父類 ----> HttpServer 父類 -----> reactor.netty.transport.ServerTransport的 bindNow方法 ----> bind方法。

重要方法 reactor.netty.transport.ServerTransport#bind

    public 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:最終調(diào)用 reactor.netty.resources.DefaultLoopResources#cacheNioServerLoops 獲得work線程池
@E2:最終調(diào)用 reactor.netty.resources.DefaultLoopResources#cacheNioSelectLoops 獲得boss線程池

問題來了:看reactor.netty.resources.LoopResources源碼,如果系統(tǒng)參數(shù)里沒有配置reactor.netty.ioSelectCount,則boss線程會和work線程池的AtomicReference在cacheNioSelectLoops 方法中返回同一對象;這是不是會造成線程池共用?

可以參看 https://blog.csdn.net/qq_42651904/article/details/134561804 這篇文章理解;

那么你們生產(chǎn)環(huán)境會單獨設(shè)置 reactor.netty.ioSelectCount 參數(shù)嗎?

jvisualvm監(jiān)控驗證

  • 如果不設(shè)置reactor.netty.ioSelectCount 這個啟動參數(shù),通過監(jiān)控網(wǎng)關(guān)啟動后線程名稱是 reactor-http-nio-xx

  • 如果在啟動的時候設(shè)置這個參數(shù)為1,啟動的線程不一樣 reactor-http-select-xx
    public static void main(String[] args) {
        System.setProperty("reactor.netty.ioSelectCount", "1");
        SpringApplication.run(XXXGatewayApp.class, args);
        log.info("XXX網(wǎng)關(guān)啟動成功!");
    }

設(shè)想一下,如果代碼寫得不好在GlobalFilter有阻塞寫法,比如數(shù)據(jù)查詢、redis查詢,加上高并發(fā)請求,那么是不是會影響boss線程“接客”?

后面再通過壓測的方式論證一下。

到此這篇關(guān)于Spring Cloud Gateway 啟動流程源碼分析的文章就介紹到這了,更多相關(guān)Spring Cloud Gateway 啟動流程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

突泉县| 荣昌县| 平果县| 长宁区| 新宁县| 宁阳县| 广昌县| 余干县| 柳江县| 鄱阳县| 乐安县| 兴安县| 青州市| 房山区| 肥乡县| 永平县| 蕉岭县| 汉川市| 平武县| 武胜县| 彭泽县| 双江| 宁明县| 札达县| 德钦县| 德兴市| 沙河市| 开封县| 宝山区| 壶关县| 海原县| 金川县| 集贤县| 达州市| 陵川县| 海阳市| 腾冲县| 登封市| 建昌县| 临潭县| 抚州市|