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));
                    } ......
    }
相关推荐
wclass-zhengge8 分钟前
SpringBoot篇(运维实用篇 - 临时属性)
运维·spring boot·后端
速盾cdn9 分钟前
速盾:什么是高防CDN?高防CDN的用处有哪些?
运维·服务器·网络·web安全
kinlon.liu36 分钟前
安全日志记录的重要性
服务器·网络·安全·安全架构·1024程序员节
海绵波波10739 分钟前
Webserver(1.6)Linux系统IO函数
linux·运维·服务器
江水三千里1 小时前
NFS服务器
运维·服务器
bossface1 小时前
申请https证书
服务器·网络协议·https·ssl
forestqq1 小时前
构建后端为etcd的CoreDNS的容器集群(七)、编写适合阅读的域名管理脚本
运维·数据库·etcd
华东设计之美1 小时前
etcd多实例配置
linux·服务器·etcd
dessler1 小时前
Linux系统-开关机
linux·运维·云计算
CYRUS STUDIO1 小时前
frida脚本,自动化寻址JNI方法
android·运维·自动化·逆向·移动安全·jni·frida