Tomcat从6.0版本开始支持NIO, 主体部分代码集中在NioEndpoint这个类当中,Tomcat NIO采用经典的反应堆模式设计,其中核心的组件包括:
1) NioEndpoint (组装器)
2) Acceptor (独立子线程)
3) Poller (独立子线程)(反应堆模式中的multiplexer的wrapper)
4) Worker (独立子线程)(反应堆模式中的dispatcher)
5) SocketProcessor (独立子线程)
6) Handler(反应堆模式中的handler)
Acceptor与Poller中间的数据缓冲是Poller中的events(类型为ConcurrentLinkedQueue<Runnable>);Poller与Worker中间没有数据缓冲,Poller直接将Channel交给Worker,而Worker也是直接将Channel交给SocketProcessor,最终Processor将通道交给Handler完成处理。(reactor模型)
这些组件交互的时序图如下:
Tomcat Nio核心流程由四大部分组成:
1) 组件初始化。
核心组件的组装都是在NioEndpoint中完成。
public class NioEndpoint {
// 代表一个服务端TCP服务的管道
protected ServerSocketChannel serverSock = null;
// 用于监控通道的多路复用器
protected Poller poller = null;
// 默认只配置一个Acceptor
protected int acceptorThreadCount = 1;
// 初始化并打开端口资源,准备接收请求
public void init() {
serverSock = ServerSocketChannel.open();
serverSock.socket().bind(addr,backlog);
...
}
public void start() {
init();
...
// 初始化Acceptor线程
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(daemon);
acceptorThread.start();
}
// 初始化Poller线程
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
}
2) 请求接收与注册。
该部分功能主要完成了新请求的接收,以及将请求添加进多路复用器进行进一步事件的监控。完成这部分功能的组件有Acceptor,Poller与NioEndpoint。Acceptor负责循环接收请求,Poller充当了多路复用器的角色,其中核心组件是Selector,而NioEndpoint完成了Acceptor与Poller组件的组装,协调与资源供给。
/**
* Acceptor为后台线程,用于接收进入的TCP/IP连接,并将连接的适配器加入待处理队列中等待Poller处理
*/
protected class Acceptor implements Runnable {
public void run() {
// 重复运行直到收到后台发来的关闭请求
while (running) {
// 如果Acceptor处于暂停状态,则循环睡眠,直到恢复正常运行状态
while (paused) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
try {
// 尝试获取新连接请求,此操作肯能会造成线程阻塞,直到有新请求到达
SocketChannel socket = serverSock.accept();
if ( running && (!paused) && socket != null ) {
// 具体连接处理逻辑
if (!setSocketOptions(socket)) {
try {
socket.socket().close();
socket.close();
} catch (IOException ix) {
}
}
}
}catch ( IOException x ) {
} catch (OutOfMemoryError oom) {
releaseCaches();
} catch (Throwable t) {
}
}//while
}//run
}
核心的函数为setSocketOptions(socket),其中不光完成了socket参数的设置,更重要的工作是将socket包装成为NioChannel装饰模型,并注册给多路复用器Poller以等待事件发生。
/**
* 请求注册
*/
protected boolean setSocketOptions(SocketChannel socket) {
try {
// 设置socket为非阻塞模式
socket.configureBlocking(false);
Socket sock = socket.socket();
// 设置socket的发送/接收缓存大小, 是否长连接等属性
socketProperties.setProperties(sock);
// 从NioChannel缓冲池中获取NioChannel, NioChannel是SocketChannel的一个wrapper,
// 目的是对上层屏蔽SSL socket channel和non SSL socket channel的差别
NioChannel channel = nioChannels.poll();
// 无法获取则新建一个NioChannel
if ( channel == null ) {
// normal tcp setup
NioBufferHandler bufhandler = new NioBufferHandler(socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
channel = new NioChannel(socket, bufhandler);
// 设置从缓冲中获取的NioChannel
} else {
channel.setIOChannel(socket);
if ( channel instanceof SecureNioChannel ) {
SSLEngine engine = createSSLEngine();
((SecureNioChannel)channel).reset(engine);
} else {
channel.reset();
}
}
// 将新接收到的socketChannel注册到selector中
getPoller0().register(channel);
} catch (Throwable t) {
return false;
}
return true;
}
前面的大段代码是设置socket参数以及将socket包装成为NioChannel,核心调用发生在getPoller0().register中,Acceptor通过NioEndpoint获得Poller以后,将包装好的NioEndpiont加入到Poller中的等待队列中,等待Poller处理注册事件
3) 事件注册,分拣与派发。
这是整个NIO事件框架的核心,有Poller完成。Poller中包含一个等待队列,其他组件可以通过向等待队列中添加需要注册读写事件的通道,Poller会循环处理队列中的事件; 并在处理完事件后调用多路复用器的查询功能,查看发生感兴趣事件的通道,并进行事件派发给合适的处理器SocketProcessor。
// 后台线程,循环处理待处理事件队列中的事件
public class Poller implements Runnable {
// 多路复用器
protected Selector selector;
// 待处理事件队列
protected ConcurrentLinkedQueue<Runnable> events = new ConcurrentLinkedQueue<Runnable>();
// 唤醒多路复用器的条件阈值
protected AtomicLong wakeupCounter = new AtomicLong(0l);
// 停止Poller线程前所需要打开的插销
protected CountDownLatch stopLatch = new CountDownLatch(1);
// 将新来的socket请求包装成统一的事件类型PollerEvent后,加入待处理事件队列中
public void register(final NioChannel socket)
{
socket.setPoller(this);
KeyAttachment key = keyCache.poll();
final KeyAttachment ka = key!=null?key:new KeyAttachment();
ka.reset(this,socket,getSocketProperties().getSoTimeout());
// 从事件池中取出一个空闲的事件对象,并使用新连接初始化事件对象
PollerEvent r = eventCache.poll();
ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
else r.reset(socket,ka,OP_REGISTER);
// 加入事件队列
addEvent(r);
}
// 外部组件可以通过该接添加待处理事件
public void addEvent(Runnable event) {
events.offer(event);
// 如果当前事件队列中没有事件,则唤醒处于阻塞状态的selector
if ( wakeupCounter.incrementAndGet() == 0 ) selector.wakeup();
}
// Poller是实现了Runnable接口的后台线程,实现了run方法
public void run() {
// 无线循环处理事件,直到接收到关闭命令
while (running) {
try {
// 如果Endpoint处于暂停状态,则将线程暂时进入睡眠状态
while (paused && (!close) ) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// Ignore
}
}
boolean hasEvents = false;
hasEvents = (hasEvents | events());
// Time to terminate?
if (close) {
timeout(0, false);
stopLatch.countDown();
return;
}
int keyCount = 0;
try {
if ( !close ) {
if (wakeupCounter.get()>0) {
//if we are here, means we have other stuff to do
//do a non blocking select
// 以非阻塞方式查看多路通道是否有事件发生
keyCount = selector.selectNow();
}else {
wakeupCounter.set( -1);
// 查看多路通道是否有事件发生,超过指定时间则立即返回
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
timeout(0, false);
stopLatch.countDown();
selector.close();
return;
}
} catch ( NullPointerException x ) {
continue;
} catch ( CancelledKeyException x ) {
continue;
} catch (Throwable x) {
continue;
}
// 此时如果没有感兴趣的通道事件发生,则再次执行待处理队列中的事件,
// 如果有事件待处理,后面就暂时不对通道执行超时检测
if ( keyCount == 0 ) hasEvents = (hasEvents | events());
Iterator iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;
// 循环处理有事件发生的channel
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = (SelectionKey) iterator.next();
KeyAttachment attachment = (KeyAttachment)sk.attachment();
// 更新通道最近一次发生事件的时间,防止因超时没有事件发生而被剔除出selector
attachment.access();
iterator.remove();
// 具体处理通道的逻辑
processKey(sk, attachment);
}//while
// 多路复用器每执行一遍完整的轮询便查看所有通道是否超时,对超时的通道将会被剔除出多路复用器
timeout(keyCount,hasEvents);
} catch (OutOfMemoryError oom) {
releaseCaches();
}
}//while
synchronized (this) {
this.notifyAll();
}
stopLatch.countDown();
}
// 处理事件队列中的所有待处理事件
public boolean events() {
boolean result = false;
Runnable r = null;
// 等待队列中是否有尚未处理的事件
result = (events.size() > 0);
// 将等待队列中的事件一一处理
while ( (r = (Runnable)events.poll()) != null ) {
try {
// 运行事件处理逻辑,此处将事件设计成为Runnable的意义是将具体的事件处理逻辑和事件框架分开
r.run();
// 事件处理完成后,将事件对象归还对象池
if ( r instanceof PollerEvent ) {
((PollerEvent)r).reset();
eventCache.offer((PollerEvent)r);
}
} catch ( Throwable x ) {
log.error("",x);
}
}
return result;
}
// 处理selector检测到的通道事件
protected boolean processKey(SelectionKey sk, KeyAttachment attachment) {
boolean result = true;
try {
if ( close ) {
cancelledKey(sk, SocketStatus.STOP, false);
} else if ( sk.isValid() && attachment != null ) {
// 确保通道不会因超时而被剔除
attachment.access();
sk.attach(attachment);
NioChannel channel = attachment.getChannel();
// 处理通道发生的读写事件
if (sk.isReadable() || sk.isWritable() ) {
if ( isWorkerAvailable() ) {
// 在通道上注销对已经发生事件的关注
unreg(sk, attachment,sk.readyOps());
// 具体通道处理逻辑
boolean close = (!processSocket(channel));
if (close) {
cancelledKey(sk,SocketStatus.DISCONNECT,false);
}
} else {
result = false;
}
}
} else {
// 解除无效通道
cancelledKey(sk, SocketStatus.ERROR,false);
}
} catch ( CancelledKeyException ckx ) {
cancelledKey(sk, SocketStatus.ERROR,false);
} catch (Throwable t) {
log.error("",t);
}
return result;
}
}
// 待处理事件队列中事件模型
public class PollerEvent implements Runnable {
protected NioChannel socket;
protected int interestOps;
protected KeyAttachment key;
public PollerEvent(NioChannel ch, KeyAttachment k, int intOps) {
reset(ch, k, intOps);
}
public void reset(NioChannel ch, KeyAttachment k, int intOps) {
socket = ch;
interestOps = intOps;
key = k;
}
public void reset() {
reset(null, null, 0);
}
public void run() {
// 该socket第一次注册到多路复用器selector中,完成对该socket的读事件注册
if ( interestOps == OP_REGISTER ) {
try {
socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, key);
} catch (Exception x) {
log.error("", x);
}
// socket之前已经注册到了selector中,更新对该socket所感兴趣的事件
} else {
final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
try {
boolean cancel = false;
if (key != null) {
final KeyAttachment att = (KeyAttachment) key.attachment();
if ( att!=null ) {
// 刷新事件的最后访问时间,防止事件超时
att.access();
int ops = key.interestOps() | interestOps;
att.interestOps(ops);
key.interestOps(ops);
} else {
cancel = true;
}
} else {
cancel = true;
}
// socket异常,从多路复用器中剔除
if ( cancel ) getPoller0().cancelledKey(key,SocketStatus.ERROR,false);
}catch (CancelledKeyException ckx) {
try {
getPoller0().cancelledKey(key,SocketStatus.DISCONNECT,true);
}catch (Exception ignore) {}
}
}//end if
}//run
}
具体的事件派发是通过Poller.run方法中调用的函数processSocket完成。
public class NioEndpoint {
protected boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
try {
// 将有事件发生的通道交给worker线程处理
getWorkerThread().assign(socket, status);
} catch (Throwable t) {
log.error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
}
processSocket实现在NioEndpoint中,Poller通过NioEndpoint的协调,将发生事件的通道交给工作线程Worker来进一步处理。整个事件框架的工作就到此结束,下面就是事件的处理。
4) 事件处理。
事件处理的核心组件有Worker,SocketProcessor和Handler。Poller将发生事件的通道交给Worker(注意这里是推的模式,而在Nginx和Jetty中采用的是拉的模式),而Worker由交给SocketProcessor处理,SocketProcessor寻找合适的Handler处理器做最终处理。
// 工作线程,负责将通道交给合适的处理器处理
protected class Worker implements Runnable {
protected Thread thread = null;
protected boolean available = false;
protected Object socket = null;
protected SocketStatus status = null;
/**
* 推模型接口
*/
protected synchronized void assign(Object socket) {
// Wait for the Processor to get the previous Socket
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
status = null;
available = true;
notifyAll();
}
/**
* 等待Poller推送通道过来
*/
protected synchronized Object await() {
// Wait for the Connector to provide a new Socket
while (!available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Notify the Connector that we have received this Socket
Object socket = this.socket;
available = false;
notifyAll();
return (socket);
}
public void run() {
// 循环处理请求,直到容器收到关闭信号
while (running) {
NioChannel socket = null;
SelectionKey key = null;
try {
// 等待分拣器将发生事件的管道分配给工作线程处理
Object channel = await();
if (channel == null)
continue;
// 将通道交给处理器处理
socket = (NioChannel)channel;
SocketProcessor sc = processorCache.poll();
if ( sc == null )
sc = new SocketProcessor(socket,status);
else sc.reset(socket,status);
sc.run();
}catch(CancelledKeyException cx) {
if (socket!=null && key!=null) socket.getPoller().cancelledKey(key,null,false);
} catch (OutOfMemoryError oom) {
releaseCaches();
} finally {
recycleWorkerThread(this);
}
}
}
/**
* 启动工作线程
*/
public void start() {
thread = new Thread(this);
thread.setName(getName() + "-" + (++curThreads));
thread.setDaemon(true);
thread.setPriority(getThreadPriority());
thread.start();
}
}
protected class SocketProcessor implements Runnable {
protected NioChannel socket = null;
protected SocketStatus status = null;
public SocketProcessor(NioChannel socket, SocketStatus status) {
reset(socket,status);
}
public void reset(NioChannel socket, SocketStatus status) {
this.socket = socket;
this.status = status;
}
public void run() {
NioEndpoint.this.activeSocketProcessors.addAndGet(1);
SelectionKey key = null;
try {
key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
if (key!=null) handshake = socket.handshake(key.isReadable(), key.isWritable());
// 交给Handler处理请求
boolean closed = (status==null)?(handler.process(socket)==Handler.SocketState.CLOSED) :
(handler.event(socket,status)==Handler.SocketState.CLOSED);
if (closed) {
// Close socket and pool
try {
KeyAttachment ka = null;
if (key!=null) {
ka = (KeyAttachment) key.attachment();
if (ka!=null) ka.setComet(false);
socket.getPoller().cancelledKey(key, SocketStatus.ERROR, false);
}
if (socket!=null) nioChannels.offer(socket);
socket = null;
if ( ka!=null ) keyCache.offer(ka);
ka = null;
}catch ( Exception x ) {
log.error("",x);
}
}
}catch ( Throwable t ) {
log.error("",t);
socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
} finally {
socket = null;
status = null;
//return to cache
processorCache.offer(this);
NioEndpoint.this.activeSocketProcessors.addAndGet(-1);
}
}
}