nio
BIO
tomcat 的 BIO 线程模型
- 使用线程池(Executor)每次有新的线程接入,启动一个线程。每个线程里头有一个阻塞的 BIO(Acceptor)
参考下 Tomcat 的设计!
Connector 启动以后会启动一组线程用于不同阶段的请求处理过程。
- Acceptor 线程组。用于接受新连接,并将新连接封装一下,选择一个 Poller 将新连接添加到 Poller 的事件队列中。
- Poller 线程组。用于监听 Socket 事件,当 Socket 可读或可写等等时,将 Socket 封装一下添加到 worker 线程池的任务队列中。
- worker 线程组。用于对请求进行处理,包括分析请求报文并创建 Request 对象,调用容器的 pipeline 进行处理。
Tomcat采用“IO线程与Worker线程通过队列解耦”类线程模型:
- 有少数几个IO线程监听上游发过来的请求,并进行收发包(生产者)
- 有一个或多个任务队列,作为IO线程与Worker线程异步解耦的数据传输通道(临界资源)
- 有多个工作线程执行正真的任务(消费者)
StandardServer 中开启 ServerSocket
Tomcat 只有一条线程负责套接字接收工作,所以它对每次接收处理的时间长短将很可能对整体性能产生影响。于是接收器所干的活都是非常少且简单的,仅仅维护了几个状态变量、流量控制闸门的累加操作、serverSocket的接收操作、设置接收到的socket的一些属性、将接收到的socket放入线程池以及一些异常处理。其他需要较长时间处理的逻辑就交给了线程池
- APR
- NIO
// 在 NIO 模式中注册到 Poller 中
getPoller0().register(channel);
Acceptor 线程组 AprEndpoint
- acceptors 数组
/** * Threads used to accept new connections and pass them to worker threads. */ protected Acceptor[] acceptors;
- 如果达到最大线程数,就加入等待队列进行等待、默认最多开启 8 * 1024 个线程
// ------------------------------------------------------------ Constructor public AprEndpoint() { // Need to override the default for maxConnections to align it with what // was pollerSize (before the two were merged) setMaxConnections(8 * 1024); }
- 如果线程数达到最大值(默认 8 * 1024) 个,则等待
protected void countUpOrAwaitConnection() throws InterruptedException { if (maxConnections==-1) return; LimitLatch latch = connectionLimitLatch; if (latch!=null) latch.countUpOrAwait(); }
- latch 里头使用 AbstractQueuedSynchronizer 计数(AQS则实现了对同步状态的管理,以及对阻塞线程进行排队,等待通知等等一些底层的实现处理)
private class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 1L; public Sync() { } @Override protected int tryAcquireShared(int ignored) { long newCount = count.incrementAndGet(); if (!released && newCount > limit) { // Limit exceeded count.decrementAndGet(); return -1; } else { return 1; } } @Override protected boolean tryReleaseShared(int arg) { count.decrementAndGet(); return true; } }
Poller 线程组
- 等待中的 Socket 放入 poller[] 轮询数组中
- 指定间隔轮询
/**
* Poll interval, in microseconds. The smaller the value, the more CPU the poller
* will use, but the more responsive to activity it will be.
*/
protected int pollTime = 2000;
// Wait for pollerTime before doing anything, so that the poller
// threads exit, otherwise parallel destruction of sockets which are
// still in the poller can cause problems
try {
this.notify();
this.wait(pollerCount * pollTime / 1000);
} catch (InterruptedException e) {
// Ignore
}
-
将 Socket 封装到 SocketProcessor 中
-
将 SocketProcessor 提交到线程池,放入线程池的 workQueue 中
worker 线程组
- ConnectionHandler.process
Processor processor = connections.get(socket);
...
do {
state = processor.process(wrapper, status);
if (state == SocketState.UPGRADING) {
....
}
} while ( state == SocketState.UPGRADING);
if (state == ....) {
...
}