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