undertow服务器初始化

springboot整合undertow服务器的源码从老生常谈的createWebServer方法谈起。spring会在生成所有bean后到创建web容器,此时会到容器找到ServletWebServerFactory接口bean,spring会根据引入的框架确定生成的ServletWebServerFactory,我们在maven中引入了undertow后,由UndertowServletWebServerFactory实现。

java 复制代码
	private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			ServletWebServerFactory factory = getWebServerFactory();
			this.webServer = factory.getWebServer(getSelfInitializer());
			getBeanFactory().registerSingleton("webServerGracefulShutdown",
					new WebServerGracefulShutdownLifecycle(this.webServer));
			getBeanFactory().registerSingleton("webServerStartStop",
					new WebServerStartStopLifecycle(this, this.webServer));
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

UndertowServletWebServerFactory类的getWebServer会创建WebServer。

java 复制代码
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		Builder builder = this.delegate.createBuilder(this);
		DeploymentManager manager = createManager(initializers);
		return getUndertowWebServer(builder, manager, getPort());
	}

先用默认的配置构建Builder对象,再使用读取的配置。

java 复制代码
Builder createBuilder(AbstractConfigurableWebServerFactory factory) {
		Ssl ssl = factory.getSsl();
		InetAddress address = factory.getAddress();
		int port = factory.getPort();
		Builder builder = Undertow.builder();
		if (this.bufferSize != null) {
			builder.setBufferSize(this.bufferSize);
		}
		if (this.ioThreads != null) {
			builder.setIoThreads(this.ioThreads);
		}
		if (this.workerThreads != null) {
			builder.setWorkerThreads(this.workerThreads);
		}
		if (this.directBuffers != null) {
			builder.setDirectBuffers(this.directBuffers);
		}
		if (ssl != null && ssl.isEnabled()) {
			new SslBuilderCustomizer(factory.getPort(), address, ssl, factory.getSslStoreProvider()).customize(builder);
			Http2 http2 = factory.getHttp2();
			if (http2 != null) {
				builder.setServerOption(UndertowOptions.ENABLE_HTTP2, http2.isEnabled());
			}
		}
		else {
			builder.addHttpListener(port, (address != null) ? address.getHostAddress() : "0.0.0.0");
		}
		builder.setServerOption(UndertowOptions.SHUTDOWN_TIMEOUT, 0);
		for (UndertowBuilderCustomizer customizer : this.builderCustomizers) {
			customizer.customize(builder);
		}
		return builder;
	}

之后再注册Servlet和Filter过滤器到容器中。

java 复制代码
    public void deploy() {
        final DeploymentInfo deploymentInfo = originalDeployment.clone();

        if (deploymentInfo.getServletStackTraces() == ServletStackTraces.ALL) {
            UndertowServletLogger.REQUEST_LOGGER.servletStackTracesAll(deploymentInfo.getDeploymentName());
        }

        deploymentInfo.validate();
        final DeploymentImpl deployment = new DeploymentImpl(this, deploymentInfo, servletContainer);
        this.deployment = deployment;

        final ServletContextImpl servletContext = new ServletContextImpl(servletContainer, deployment);
        deployment.setServletContext(servletContext);
        handleExtensions(deploymentInfo, servletContext);

        final List<ThreadSetupHandler> setup = new ArrayList<>();
        setup.add(ServletRequestContextThreadSetupAction.INSTANCE);
        setup.add(new ContextClassLoaderSetupAction(deploymentInfo.getClassLoader()));
        setup.addAll(deploymentInfo.getThreadSetupActions());
        deployment.setThreadSetupActions(setup);

        deployment.getServletPaths().setWelcomePages(deploymentInfo.getWelcomePages());

        if (deploymentInfo.getDefaultEncoding() != null) {
            deployment.setDefaultCharset(Charset.forName(deploymentInfo.getDefaultEncoding()));
        }
        if(deploymentInfo.getDefaultRequestEncoding() != null) {
            deployment.setDefaultRequestCharset(Charset.forName(deploymentInfo.getDefaultRequestEncoding()));
        } else if (deploymentInfo.getDefaultEncoding() != null) {
            deployment.setDefaultRequestCharset(Charset.forName(deploymentInfo.getDefaultEncoding()));
        }
        if(deploymentInfo.getDefaultResponseEncoding() != null) {
            deployment.setDefaultResponseCharset(Charset.forName(deploymentInfo.getDefaultResponseEncoding()));
        } else if (deploymentInfo.getDefaultEncoding() != null) {
            deployment.setDefaultResponseCharset(Charset.forName(deploymentInfo.getDefaultEncoding()));
        }

        handleDeploymentSessionConfig(deploymentInfo, servletContext);

        deployment.setSessionManager(deploymentInfo.getSessionManagerFactory().createSessionManager(deployment));
        deployment.getSessionManager().setDefaultSessionTimeout(deploymentInfo.getDefaultSessionTimeout());


        try {
            deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() {
                @Override
                public Void call(HttpServerExchange exchange, Object ignore) throws Exception {
                    final ApplicationListeners listeners = createListeners();
                    listeners.start();

                    deployment.setApplicationListeners(listeners);

                    //now create the servlets and filters that we know about. We can still get more later
                    createServletsAndFilters(deployment, deploymentInfo);

                    //first initialize the temp dir
                    initializeTempDir(servletContext, deploymentInfo);

                    //then run the SCI's
                    for (final ServletContainerInitializerInfo sci : deploymentInfo.getServletContainerInitializers()) {
                        final InstanceHandle<? extends ServletContainerInitializer> instance = sci.getInstanceFactory().createInstance();
                        try {
                            instance.getInstance().onStartup(sci.getHandlesTypes(), servletContext);
                        } finally {
                            instance.release();
                        }
                    }

                    deployment.getSessionManager().registerSessionListener(new SessionListenerBridge(deployment, listeners, servletContext));
                    for(SessionListener listener : deploymentInfo.getSessionListeners()) {
                        deployment.getSessionManager().registerSessionListener(listener);
                    }

                    initializeErrorPages(deployment, deploymentInfo);
                    initializeMimeMappings(deployment, deploymentInfo);
                    listeners.contextInitialized();
                    //run

                    HttpHandler wrappedHandlers = ServletDispatchingHandler.INSTANCE;
                    wrappedHandlers = wrapHandlers(wrappedHandlers, deploymentInfo.getInnerHandlerChainWrappers());
                    wrappedHandlers = new RedirectDirHandler(wrappedHandlers, deployment.getServletPaths());
                    if(!deploymentInfo.isSecurityDisabled()) {
                        HttpHandler securityHandler = setupSecurityHandlers(wrappedHandlers);
                        wrappedHandlers = new PredicateHandler(DispatcherTypePredicate.REQUEST, securityHandler, wrappedHandlers);
                    }
                    HttpHandler outerHandlers = wrapHandlers(wrappedHandlers, deploymentInfo.getOuterHandlerChainWrappers());
                    wrappedHandlers = new PredicateHandler(DispatcherTypePredicate.REQUEST, outerHandlers, wrappedHandlers);
                    wrappedHandlers = handleDevelopmentModePersistentSessions(wrappedHandlers, deploymentInfo, deployment.getSessionManager(), servletContext);

                    MetricsCollector metrics = deploymentInfo.getMetricsCollector();
                    if(metrics != null) {
                        wrappedHandlers = new MetricsChainHandler(wrappedHandlers, metrics, deployment);
                    }
                    if( deploymentInfo.getCrawlerSessionManagerConfig() != null ) {
                        wrappedHandlers = new CrawlerSessionManagerHandler(deploymentInfo.getCrawlerSessionManagerConfig(), wrappedHandlers);
                    }

                    final ServletInitialHandler servletInitialHandler = SecurityActions.createServletInitialHandler(deployment.getServletPaths(), wrappedHandlers, deployment, servletContext);

                    HttpHandler initialHandler = wrapHandlers(servletInitialHandler, deployment.getDeploymentInfo().getInitialHandlerChainWrappers());
                    initialHandler = new HttpContinueReadHandler(initialHandler);
                    if(deploymentInfo.getUrlEncoding() != null) {
                        initialHandler = Handlers.urlDecodingHandler(deploymentInfo.getUrlEncoding(), initialHandler);
                    }
                    deployment.setInitialHandler(initialHandler);
                    deployment.setServletHandler(servletInitialHandler);
                    deployment.getServletPaths().invalidate(); //make sure we have a fresh set of servlet paths
                    servletContext.initDone();
                    return null;
                }
            }).call(null, null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //any problems with the paths won't get detected until the data is initialize
        //so we force initialization here
        deployment.getServletPaths().initData();
        for(ServletContextListener listener : deploymentInfo.getDeploymentCompleteListeners()) {
            listener.contextInitialized(new ServletContextEvent(servletContext));
        }
        state = State.DEPLOYED;
    }

WebServer接口的start()方法就会启动undertow服务器了,本质就是通过XNIO框架监听服务器端口号,接收请求并处理。处理请求时的io线程模型分数据处理线程和业务处理线程。

java 复制代码
    public synchronized void start() {
        UndertowLogger.ROOT_LOGGER.infof("starting server: %s", Version.getFullVersionString());
        xnio = Xnio.getInstance(Undertow.class.getClassLoader());
        channels = new ArrayList<>();
        try {
            if (internalWorker) {
                worker = xnio.createWorker(OptionMap.builder()
                        .set(Options.WORKER_IO_THREADS, ioThreads)
                        .set(Options.CONNECTION_HIGH_WATER, 1000000)
                        .set(Options.CONNECTION_LOW_WATER, 1000000)
                        .set(Options.WORKER_TASK_CORE_THREADS, workerThreads)
                        .set(Options.WORKER_TASK_MAX_THREADS, workerThreads)
                        .set(Options.TCP_NODELAY, true)
                        .set(Options.CORK, true)
                        .addAll(workerOptions)
                        .getMap());
            }

            OptionMap socketOptions = OptionMap.builder()
                    .set(Options.WORKER_IO_THREADS, worker.getIoThreadCount())
                    .set(Options.TCP_NODELAY, true)
                    .set(Options.REUSE_ADDRESSES, true)
                    .set(Options.BALANCING_TOKENS, 1)
                    .set(Options.BALANCING_CONNECTIONS, 2)
                    .set(Options.BACKLOG, 1000)
                    .addAll(this.socketOptions)
                    .getMap();

            OptionMap serverOptions = OptionMap.builder()
                    .set(UndertowOptions.NO_REQUEST_TIMEOUT, 60 * 1000)
                    .addAll(this.serverOptions)
                    .getMap();


            ByteBufferPool buffers = this.byteBufferPool;
            if (buffers == null) {
                buffers = new DefaultByteBufferPool(directBuffers, bufferSize, -1, 4);
            }

            listenerInfo = new ArrayList<>();
            for (ListenerConfig listener : listeners) {
                UndertowLogger.ROOT_LOGGER.debugf("Configuring listener with protocol %s for interface %s and port %s", listener.type, listener.host, listener.port);
                final HttpHandler rootHandler = listener.rootHandler != null ? listener.rootHandler : this.rootHandler;
                OptionMap socketOptionsWithOverrides = OptionMap.builder().addAll(socketOptions).addAll(listener.overrideSocketOptions).getMap();
                ......
                    if (listener.type == ListenerType.HTTP) {
                        HttpOpenListener openListener = new HttpOpenListener(buffers, undertowOptions);
                        HttpHandler handler = rootHandler;
                        if (http2) {
                            handler = new Http2UpgradeHandler(handler);
                        }
                        openListener.setRootHandler(handler);
                        final ChannelListener<StreamConnection> finalListener;
                        if (listener.useProxyProtocol) {
                            finalListener = new ProxyProtocolOpenListener(openListener, null, buffers, OptionMap.EMPTY);
                        } else {
                            finalListener = openListener;
                        }

                        ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(finalListener);
                        AcceptingChannel<? extends StreamConnection> server = worker.createStreamConnectionServer(new InetSocketAddress(Inet4Address.getByName(listener.host), listener.port), acceptListener, socketOptionsWithOverrides);
                        server.resumeAccepts();
                        channels.add(server);
                        listenerInfo.add(new ListenerInfo("http", server.getLocalAddress(), openListener, null, server));
                    } ......
    }
相关推荐
SelectDB15 小时前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
zzzzzz3102 天前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode2 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220703 天前
如何搭建本地yum源(上)
运维
大树886 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠6 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质6 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz6 天前
Maven依赖冲突
java·服务器·maven
Inhand陈工6 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智6 天前
ARP代理--工作原理
运维·网络·arp·arp代理