1. 初始化
首先我们先明确,当没有任何设置的情况下undertow默认会获取JVM运行环境中的可用计算资源作为IO线程设置的依据(但保证最低不会少为2个),和16个工作线程。
这一步是在Spring容器启动时,通过onRefresh()
方法完成的
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
调用getWebServer
方法
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
调用createBuilder
方法
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
DeploymentManager manager = createDeploymentManager(initializers);
int port = getPort();
Builder builder = createBuilder(port);
return getUndertowWebServer(builder, manager, port);
}
private Builder createBuilder(int port) {
// 调用具体构建方法
Builder builder = Undertow.builder();
if (this.bufferSize != null) {
builder.setBufferSize(this.bufferSize);
}
// 如果ioThreads不为空,说明有单独配置过,否则就使用默认值
if (this.ioThreads != null) {
builder.setIoThreads(this.ioThreads);
}
// workerThreads同上
if (this.workerThreads != null) {
builder.setWorkerThreads(this.workerThreads);
}
if (this.directBuffers != null) {
builder.setDirectBuffers(this.directBuffers);
}
if (getSsl() != null && getSsl().isEnabled()) {
customizeSsl(builder);
}
else {
builder.addHttpListener(port, getListenAddress());
}
for (UndertowBuilderCustomizer customizer : this.builderCustomizers) {
customizer.customize(builder);
}
return builder;
}
public static Builder builder() {
return new Builder();
}
private Builder() {
// 设置默认ioThreads与workerThreads线程数的方法
ioThreads = Math.max(Runtime.getRuntime().availableProcessors(), 2);
workerThreads = ioThreads * 8;
long maxMemory = Runtime.getRuntime().maxMemory();
//smaller than 64mb of ram we use 512b buffers
if (maxMemory < 64 * 1024 * 1024) {
//use 512b buffers
directBuffers = false;
bufferSize = 512;
} else if (maxMemory < 128 * 1024 * 1024) {
//use 1k buffers
directBuffers = true;
bufferSize = 1024;
} else {
//use 16k buffers for best performance
//as 16k is generally the max amount of data that can be sent in a single write() call
directBuffers = true;
bufferSize = 1024 * 16 - 20; //the 20 is to allow some space for protocol headers, see UNDERTOW-1209
}
}
验证一下
物理机6核12线程
XNIO-1 Accept
XNIO-1 I/O-1
XNIO-2 I/O-1~12
XNIO-2 Accept
可以看出一组是12个I/O线程。
2. 线程创建逻辑
线程是在执行finishRefresh
方法的时候创建的
@Override
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
private WebServer startWebServer() {
WebServer webServer = this.webServer;
if (webServer != null) {
// 关键方法
webServer.start();
}
return webServer;
}
@Override
public void start() throws WebServerException {
synchronized (this.monitor) {
if (this.started) {
return;
}
try {
if (!this.autoStart) {
return;
}
if (this.undertow == null) {
this.undertow = createUndertowServer();
}
// 关键方法
this.undertow.start();
this.started = true;
UndertowServletWebServer.logger
.info("Undertow started on port(s) " + getPortsDescription()
+ " with context path '" + this.contextPath + "'");
}
catch (Exception ex) {
try {
if (findBindException(ex) != null) {
List<Port> failedPorts = getConfiguredPorts();
List<Port> actualPorts = getActualPorts();
failedPorts.removeAll(actualPorts);
if (failedPorts.size() == 1) {
throw new PortInUseException(
failedPorts.iterator().next().getNumber());
}
}
throw new WebServerException("Unable to start embedded Undertow", ex);
}
finally {
stopSilently();
}
}
}
}
public synchronized void start() {
UndertowLogger.ROOT_LOGGER.debugf("starting undertow server %s", this);
xnio = Xnio.getInstance(Undertow.class.getClassLoader());
channels = new ArrayList<>();
try {
if (internalWorker) {
// 关键方法
worker = xnio.createWorker(OptionMap.builder()
// 完成OptionMap的赋值
.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());
}
// 。。。省略部分代码
} catch (Exception e) {
if(internalWorker && worker != null) {
worker.shutdownNow();
}
throw new RuntimeException(e);
}
}
public XnioWorker createWorker(OptionMap optionMap) throws IOException, IllegalArgumentException {
return createWorker(null, optionMap);
}
public XnioWorker createWorker(ThreadGroup threadGroup, OptionMap optionMap) throws IOException, IllegalArgumentException {
return createWorker(threadGroup, optionMap, null);
}
public XnioWorker createWorker(final ThreadGroup threadGroup, final OptionMap optionMap, final Runnable terminationTask) throws IOException, IllegalArgumentException {
// 逻辑在构造方法中
final NioXnioWorker worker = new NioXnioWorker(this, threadGroup, optionMap, terminationTask);
worker.start();
return worker;
}
NioXnioWorker(final NioXnio xnio, final ThreadGroup threadGroup, final OptionMap optionMap, final Runnable terminationTask) throws IOException {
// super中完成worker线程的创建
super(xnio, threadGroup, optionMap, terminationTask);
// 下面的代码中完成I/O线程的创建
final int threadCount;
// WORKER_IO_THREADS在前几步start的方法中完成了赋值OptionMap.builder().set(Options.WORKER_IO_THREADS, ioThreads)...
if (optionMap.contains(Options.WORKER_IO_THREADS)) {
threadCount = optionMap.get(Options.WORKER_IO_THREADS, 0);
} else {
threadCount = Math.max(optionMap.get(Options.WORKER_READ_THREADS, 1), optionMap.get(Options.WORKER_WRITE_THREADS, 1));
}
if (threadCount < 0) {
throw log.optionOutOfRange("WORKER_IO_THREADS");
}
final long workerStackSize = optionMap.get(Options.STACK_SIZE, 0L);
if (workerStackSize < 0L) {
throw log.optionOutOfRange("STACK_SIZE");
}
final String workerName = getName();
WorkerThread[] workerThreads;
workerThreads = new WorkerThread[threadCount];
final boolean markWorkerThreadAsDaemon = optionMap.get(Options.THREAD_DAEMON, false);
boolean ok = false;
try {
// 通过for循环的方式,一个一个的构建WorkerThread对象,并挨个填充到数组中
for (int i = 0; i < threadCount; i++) {
final WorkerThread workerThread = new WorkerThread(this, xnio.mainSelectorCreator.open(), String.format("%s I/O-%d", workerName, Integer.valueOf(i + 1)), threadGroup, workerStackSize, i);
// Mark as daemon if the Options.THREAD_DAEMON has been set
if (markWorkerThreadAsDaemon) {
workerThread.setDaemon(true);
}
workerThreads[i] = workerThread;
}
// 最后构建一个accept线程
acceptThread = new WorkerThread(this, xnio.mainSelectorCreator.open(), String.format("%s Accept", workerName), threadGroup, workerStackSize, threadCount);
if (markWorkerThreadAsDaemon) {
acceptThread.setDaemon(true);
}
ok = true;
} finally {
if (! ok) {
for (WorkerThread worker : workerThreads) {
if (worker != null) safeClose(worker.getSelector());
}
}
}
this.workerThreads = workerThreads;
// 。。。省略部分代码
}
protected XnioWorker(final Xnio xnio, final ThreadGroup threadGroup, final OptionMap optionMap, final Runnable terminationTask) {
this.xnio = xnio;
this.terminationTask = terminationTask;
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(CREATE_WORKER_PERMISSION);
}
String workerName = optionMap.get(Options.WORKER_NAME);
if (workerName == null) {
workerName = "XNIO-" + seq.getAndIncrement();
}
name = workerName;
taskQueue = new LinkedBlockingQueue<Runnable>();
this.coreSize = optionMap.get(Options.WORKER_TASK_CORE_THREADS, 4);
final boolean markThreadAsDaemon = optionMap.get(Options.THREAD_DAEMON, false);
// WORKER_TASK_MAX_THREADS在前几步start的方法中完成了赋值OptionMap.builder().set(Options.WORKER_TASK_MAX_THREADS, workerThreads)...
final int threadCount = optionMap.get(Options.WORKER_TASK_MAX_THREADS, 16);
// 直接构建一个TaskPool对象,实际上就是ThreadPoolExecutor
taskPool = new TaskPool(
threadCount, // ignore core threads setting, always fill to max
threadCount,
optionMap.get(Options.WORKER_TASK_KEEPALIVE, 60000), TimeUnit.MILLISECONDS,
taskQueue,
new WorkerThreadFactory(threadGroup, optionMap, markThreadAsDaemon),
new ThreadPoolExecutor.AbortPolicy());
}
final class TaskPool extends ThreadPoolExecutor {
TaskPool(final int corePoolSize, final int maximumPoolSize, final long keepAliveTime, final TimeUnit unit, final BlockingQueue<Runnable> workQueue, final ThreadFactory threadFactory, final RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
protected void terminated() {
taskPoolTerminated();
}
}
worker线程在服务启动时并不会立刻启动,只有当真正发生调用时才会启动,如下图所示,当产生一次请求时,就启动一个task线程来处理
再一次请求,就再启动一个,直接启满指定的数量。
3. 调整线程数量
当然也可以自定义I/O、worker
线程。
server.undertow.io-threads=2
server.undertow.worker-threads=4
此时最多可以同时处理4个请求,所以如果同时请求超过4个,那超过的部分就只能等待其他请求处理完成。
所以worker
线程一般都会设置的多一点,当然如果要得到科学的数值,就只能根据实际压测效果来确定,源码中默认为I/O
线程的8倍也只是经验值。
4. Worker线程和I/O线程
首先从源码中我们可以看出,无论是Worker线程还是I/O线程都是由XnioWorker
类统一管理。
I/O线程通常处理非阻塞请求,而Worker线程会被线程池统一管理,负责处理阻塞请求,例如:Servlet调用。
从源码上来看,I/O线程负责接收所有的请求任务,并将任务添加到一个名为selectorWorkQueue
的队列中,此时I/O线程的一次任务就结束了,之后从队列中取出的任务会提交给管理Worker线程的线程池处理。
5. Accept线程
Accept线程就和NIO中的Accept线程一样,主要负责将channel
注册到selector
中,并监听客户端的accept请求。