Tomcat NIO源代码分析(二) -- Poller

接着上面的流程,现在请求到了Poller的#register()方法

	public void register(final NioChannel socket) {
socket.setPoller(this);
// KeyAttachment是对NioChannel信息的包装,同样是非GC
KeyAttachment key = keyCache.poll();
final KeyAttachment ka = key != null ? key : new KeyAttachment(socket);
ka.reset(this, socket, getSocketProperties().getSoTimeout());
ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());

// PollerEvent的初始化,非GC Again
PollerEvent r = eventCache.poll();
// this is what OP_REGISTER turns into.
// 读取数据的事件
ka.interestOps(SelectionKey.OP_READ);
if (r == null)
r = new PollerEvent(socket, ka, OP_REGISTER);
else
r.reset(socket, ka, OP_REGISTER);

// 把事件加到Poller
addEvent(r);
}

public void addEvent(Runnable event) {
// 把事件加入到队列中
events.offer(event);
// ++wakeupCounter
if (wakeupCounter.incrementAndGet() == 0) selector.wakeup();
}


其实也挺好懂的,就是把NioChannel作为OP_REGISTER事件注册到Poller,这样在Poller的#run()方法中就可以对加入Poller的事件进行处理了

	public void run() {
while (running) {
try {
while (paused && (!close)) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Ignore
}
}
boolean hasEvents = false;

hasEvents = (hasEvents | events());
// Time to terminate?
if (close) {
timeout(0, false);
break;
}
try {
if (!close) {
if (wakeupCounter.get() > 0) {
// 立刻返回 I/O 就绪的那些通道的键
keyCount = selector.selectNow();
} else {
keyCount = selector.keys().size();
// 这里把wakeupCounter设成-1,在addEvent的时候就会唤醒selector
wakeupCounter.set(-1);
// 使用阻塞的方式
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
timeout(0, false);
selector.close();
break;
}
} catch (NullPointerException x) {
// sun bug 5076772 on windows JDK 1.5
if (log.isDebugEnabled())
log.debug("Possibly encountered sun bug 5076772 on windows JDK 1.5", x);
if (wakeupCounter == null || selector == null)
throw x;
continue;
} catch (CancelledKeyException x) {
// sun bug 5076772 on windows JDK 1.5
if (log.isDebugEnabled())
log.debug("Possibly encountered sun bug 5076772 on windows JDK 1.5", x);
if (wakeupCounter == null || selector == null)
throw x;
continue;
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error("", x);
continue;
}
// either we timed out or we woke up, process events first
if (keyCount == 0)
hasEvents = (hasEvents | events());

Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator()
: null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
// 这里的KeyAttachment实在#register()方法中注册的
KeyAttachment attachment = (KeyAttachment) sk.attachment();
attachment.access();
iterator.remove();
// 继续流程
processKey(sk, attachment);
}// while

// process timeouts
timeout(keyCount, hasEvents);
if (oomParachute > 0 && oomParachuteData == null)
checkParachute();
} catch (OutOfMemoryError oom) {
try {
oomParachuteData = null;
releaseCaches();
log.error("", oom);
} catch (Throwable oomt) {
try {
System.err.println(oomParachuteMsg);
oomt.printStackTrace();
} catch (Throwable letsHopeWeDontGetHere) {
ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
}
}
}
}// while
synchronized (this) {
this.notifyAll();
}
stopLatch.countDown();

}


这个方法有2个方法需要关注一下:#events()和#processKey():

	public boolean events() {
boolean result = false;
// synchronized (events) {
Runnable r = null;
// 返回是事件队列中是否有事件
result = (events.size() > 0);
while ((r = events.poll()) != null) {
try {
// 执行KeyEvent的#run()
r.run();
if (r instanceof PollerEvent) {
((PollerEvent) r).reset();
// 对KeyEvent进行回收
eventCache.offer((PollerEvent) r);
}
} catch (Throwable x) {
log.error("", x);
}
}
// events.clear();
// }
return result;
}


这里执行了SocketChannel对应的KeyEvent的#run()方法,在这个方法里给SocketChannel注册了OP_READ:

	public void run() {
if (interestOps == OP_REGISTER) {
try {
// 给SocketChannel注册OP_READ
socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ,
key);
} catch (Exception x) {
log.error("", x);
}
} else {
// 这里应该是对comet进行支持的,暂时先不看

......

}// end if
}// run


第二个是#processKey()方法,里边的很多流程我现在不是很关心,都略去了,

	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();// make sure we don't time out valid sockets
sk.attach(attachment);// cant remember why this is here
NioChannel channel = attachment.getChannel();
if (sk.isReadable() || sk.isWritable()) {
if (attachment.getSendfileData() != null) {
processSendfile(sk, attachment, true, false);
} else if (attachment.getComet()) {// 这里应该是对comet的支持
......
} else {
// 这个分支是现在比较关心的
if (isWorkerAvailable()) {// 这个好像还没实现
// 这个#unreg()很巧妙,防止了通道对同一个事件不断select的问题
unreg(sk, attachment, sk.readyOps());
boolean close = (!processSocket(channel, null, true));
if (close) {
cancelledKey(sk, SocketStatus.DISCONNECT, false);
}
} else {
result = false;
}
}
}
} else {
// invalid key
cancelledKey(sk, SocketStatus.ERROR, false);
}
} catch (CancelledKeyException ckx) {
cancelledKey(sk, SocketStatus.ERROR, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("", t);
}
return result;
}

protected void unreg(SelectionKey sk, KeyAttachment attachment, int readyOps) {
reg(sk, attachment, sk.interestOps() & (~readyOps));
}

protected void reg(SelectionKey sk, KeyAttachment attachment, int intops) {
sk.interestOps(intops);
attachment.interestOps(intops);
attachment.setCometOps(intops);
}


这里的#unreg()方法据我理解应该很巧妙的解决了重复的IO事件问题,我自己写的测试用的NIO代码里就会有这个问题。

这样,流程就来到了Poller最后的#processSocket()方法了:

	public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
try {
KeyAttachment attachment = (KeyAttachment) socket.getAttachment(false);
attachment.setCometNotify(false); // will get reset upon next reg
// 使用SocketProcessor
SocketProcessor sc = processorCache.poll();
if (sc == null)
sc = new SocketProcessor(socket, status);
else
sc.reset(socket, status);
if (dispatch && getExecutor() != null)// 如果配置了ThreadPoolExecutor,那么使用它来执行
getExecutor().execute(sc);
else
sc.run();
} catch (RejectedExecutionException rx) {
log.warn("Socket processing request was rejected for:" + socket, rx);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
log.error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}


这里SocketProcessor的#run()方法就不列出了,里边最后会通过下面的语句将流程转到Http11NioProtocol类,其中的handler就是对Http11NioProtocol的引用:

        SocketState state = SocketState.OPEN;
state = (status==null)?handler.process(socket):handler.event(socket,status);


最后,对Acceptor和Poller的处理过程做个小结,见下图:

[img]http://dl.iteye.com/upload/attachment/362781/dbbad849-bc36-304a-9f7e-0f9ba2b49a78.png[/img]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值