undertow初始化线程资源解析

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请求。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码拉松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值