0. Poller
// org.apache.tomcat.util.net.NioEndpoint.Poller
public class Poller implements Runnable {
// 每个 poller 开启一个 Selector
private Selector selector;
public Poller() throws IOException {
this.selector = Selector.open();
}
// poller借助事件来行动的
private final SynchronizedQueue<PollerEvent> events =
new SynchronizedQueue<>();
public boolean events() {
boolean result = false;
PollerEvent pe = null;
for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
result = true;
try {
// step into ...
pe.run();
// 这里也是如同前面说到的缓存作用,后继可以复用
pe.reset();
if (running && !paused) {
// eventCache 是其外部类的属性
// private SynchronizedStack<PollerEvent> eventCache;
eventCache.push(pe);
}
} catch ( Throwable x ) {
log.error("",x);
}
}
return result;
}
private volatile boolean close = false;
private long nextExpiration = 0;//optimize expiration handling
// 初始值0
private AtomicLong wakeupCounter = new AtomicLong(0);
private volatile int keyCount = 0;
// 我们在acceptor的轮询过程将channel最终注册到poller上了
public void register(final NioChannel socket) {
socket.setPoller(this);
NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
socket.setSocketWrapper(ka);
ka.setPoller(this);
ka.setReadTimeout(getConnectionTimeout());
ka.setWriteTimeout(getConnectionTimeout());
ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
ka.setSecure(isSSLEnabled());
PollerEvent r = eventCache.pop();
// 设置注册到 selector 的偏好
ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
// pollerEvent 设置 注册事件
if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
else r.reset(socket,ka,OP_REGISTER);
addEvent(r);
}
/**
* The background thread that adds sockets to the Poller, checks the
* poller for triggered events and hands the associated socket off to an
* appropriate processor as events occur.
*/
@Override
public void run() {
// 循环直到 destroy() 被调用
// Loop until destroy() is called
while (true) {
boolean hasEvents = false;
try {
if (!close) {
// 首先会调用 events() 滚一遍pollerEvents
hasEvents = events();
// AtomicLong.getAndSet() 返回被修改前的值
if (wakeupCounter.getAndSet(-1) > 0) {
// 此时说明 wakeupCounter 原先值是大于0的,那么直接无阻塞的select
//if we are here, means we have other stuff to do
//do a non blocking select
keyCount = selector.selectNow();
} else {
// 阻塞的select
// 当前还未被唤醒
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
events();
timeout(0, false);
try {
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
} 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());
// 刚刚select检出的selectionKeys
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();
NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (attachment == null) {
iterator.remove();
} else {
iterator.remove();
// 处理selectionKey
processKey(sk, attachment);
}
}//while
//process timeouts
timeout(keyCount,hasEvents);
}//while
getStopLatch().countDown();
}
protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
try {
if ( close ) {
cancelledKey(sk);
} else if ( sk.isValid() && attachment != null ) {
// 我们关心运行过程的状态
if (sk.isReadable() || sk.isWritable() ) {
if ( attachment.getSendfileData() != null ) {
// 这个掠过
processSendfile(sk,attachment, false);
} else {
unreg(sk, attachment, sk.readyOps());
boolean closeSocket = false;
// Read goes before write
// 分别处理读、写
if (sk.isReadable()) {
// 读、写的入参列表几乎一致,仅仅是socketEvent的差异
if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
closeSocket = true;
}
}
if (!closeSocket && sk.isWritable()) {
if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
closeSocket = true;
}
}
if (closeSocket) {
cancelledKey(sk);
}
}
}
} else {
//invalid key
cancelledKey(sk);
}
} catch ( CancelledKeyException ckx ) {
cancelledKey(sk);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("",t);
}
}
}
1. PollerEvent
PollerEvent也是Runnable
// org.apache.tomcat.util.net.NioEndpoint.PollerEvent
public static class PollerEvent implements Runnable {
private NioChannel socket;
private int interestOps;
private NioSocketWrapper socketWrapper;
@Override
public void run() {
if (interestOps == OP_REGISTER) {
// 注册事件这边请
try {
// selector 注册偏好 读
socket.getIOChannel().register(
socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
} catch (Exception x) {
log.error(sm.getString("endpoint.nio.registerFail"), x);
}
} else {
final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
try {
if (key == null) {
// The key was cancelled (e.g. due to socket closure)
// and removed from the selector while it was being
// processed. Count down the connections at this point
// since it won't have been counted down when the socket
// closed.
socket.socketWrapper.getEndpoint().countDownConnection();
} else {
final NioSocketWrapper socketWrapper = (NioSocketWrapper) key.attachment();
if (socketWrapper != null) {
//we are registering the key to start with, reset the fairness counter.
int ops = key.interestOps() | interestOps;
socketWrapper.interestOps(ops);
key.interestOps(ops);
} else {
socket.getPoller().cancelledKey(key);
}
}
} catch (CancelledKeyException ckx) {
try {
socket.getPoller().cancelledKey(key);
} catch (Exception ignore) {}
}
}
}
}
2. AbstractEndpoint#processSocket
// org.apache.tomcat.util.net.AbstractEndpoint#processSocket
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
// protected SynchronizedStack<SocketProcessorBase<S>> processorCache;
SocketProcessorBase<S> sc = processorCache.pop();
if (sc == null) {
// public abstract class SocketProcessorBase<S> implements Runnable
// 业务逻辑的切入点即 该runnable.run 调用模板方法 doRun()
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
// 前面tomcat自己构造的1个工作线程池
Executor executor = getExecutor();
if (dispatch && executor != null) {
// 执行这个runnable
executor.execute(sc);
} else {
sc.run();
}
} catch (RejectedExecutionException ree) {
getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
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
getLog().error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
poller.run()逻辑
- 先来个pollerEvents循环,将其channel注册到selector
- 然后是SelectionKeys循环,启动工作线程,处理selectionKey
TomcatNioEndpoint的Poller机制解析
文章详细介绍了ApacheTomcat中NioEndpoint的Poller组件的工作原理。Poller是一个实现了Runnable的类,它维护了一个Selector用于监听通道事件。PollerEvent负责将NioChannel注册到Selector上,而Selector在轮询过程中处理读写事件。当事件发生时,Poller启动工作线程处理Socket事件,如读写操作,通过SocketProcessorBase执行实际业务逻辑。

被折叠的 条评论
为什么被折叠?



