Tomcat中Poller线程run方法工作流程
1. Poller线程run方法工作流程概述:
(1). 查询Poller线程同步队列中是否存在事件。存在, 即把NioSocketWrapper注册到Poller线程中的Selector中。
(2). 遍历已经就绪的SelectorKey集合,分发处理所有活跃的事件。
(3). 把SocketProcessorBase线程交给线程池执行。
2. Poller线程什么时候启动?
Poller线程启动调用链如下:
startInternal:341, NioEndpoint{org.apache.tomcat.util.net}代码如下:
@Override
public void startInternal() throws Exception {
......
// Start poller thread
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
......
}
Tomcat容器一键式启动,在启动Connector连接器时,会先去启动Endpoint,NioEndpoint.startInternal()方法中调用Thread线程的start()方法,Poller线程启动。
3. Poller.run()方法调用链源码跟踪
3.1 NioEndpoint$Poller.run()
run:851, NioEndpoint$Poller{org.apache.tomcat.util.net}
/**
* 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() {
// Loop until destroy() is called
while (true) {
boolean hasEvents = false;
try {
if (!close) {
// 如果Poller线程未关闭,查询Poller线程中同步队列SynchronizedQueue<PollerEvent>中的是否有事件。
// 如果存在的话,events()方法从PollerEvent中取出NioSocketWrapper注册到Poller线程中的selector中
hasEvents = events();
// private AtomicLong wakeupCounter = new AtomicLong(0);
// getAndSet: 通过CAS的方式将给定的值设置新的值,并且返回旧的值
if (wakeupCounter.getAndSet(-1) > 0) {
// If we are here, means we have other stuff to do. Do a non blocking select
// 如果代码走到了这里,意味着我们有其他工作去做,做一个非阻塞的select操作
keyCount = selector.selectNow();
} else {
// 正常情况下wakeupCounter.getAndSet(-1)返回初始值0,然后做一个阻塞时间1秒的select操作
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
// 查询是否有时间
events();
timeout(0, false);
try {
// 关闭selector
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
// Either we timed out or we woke up, process events first
if (keyCount == 0) {
hasEvents = (hasEvents | events());
}
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error(sm.getString("endpoint.nio.selectorLoopError"), x);
continue;
}
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
// 遍历已经就绪的SelectorKey集合,分发处理所有活跃的事件
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
iterator.remove();
// 从SelectionKey中取回附属物,NioSocketWrapper继承了SocketWrapperBase,强转成NioSocketWrapper
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (socketWrapper != null) {
// 处理事件
processKey(sk, socketWrapper);
}
}
// Process timeouts
timeout(keyCount,hasEvents);
}
getStopLatch().countDown();
}
从上述代码中可以看到Poller线程#run方法所涉及的步骤大致如下:
(1). 如果Poller线程未关闭,查询Poller线程中同步队列中的是否存在事件。如果存在的话,从PollerEvent的属性中取出NioSocketWrapper,把NioSocketWrapper注册到Poller线程中的Selector中。
(2). 遍历已经就绪的SelectorKey集合,从SelectionKey中取回附属物NioSocketWrapper,然后分发处理所有活跃的事件。
3.1.1 NioEndpoint.Poller.events()
/**
* Processes events in the event queue of the Poller.
*
* @return <code>true</code> if some events were processed,
* <code>false</code> if queue was empty
*/
public boolean events() {
boolean result = false;
// PollerEvent的作用:缓存Poller事件,避免GC回收
PollerEvent pe = null;
for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
result = true;
// 从PollerEvent中得到NioSocketWrapper
NioSocketWrapper socketWrapper = pe.getSocketWrapper();
// NioSocketWrapper继承了SocketWrapperBase,它里面有属性Socket
SocketChannel sc = socketWrapper.getSocket().getIOChannel();
int interestOps = pe.getInterestOps();
if (sc == null) {
log.warn(sm.getString("endpoint.nio.nullSocketChannel"));
socketWrapper.close();
} else if (interestOps == OP_REGISTER) {
try {
// 把NioSocketWrapper注册到selector中,操作是读操作
sc.register(getSelector(), SelectionKey.OP_READ, socketWrapper);
} catch (Exception x) {
log.error(sm.getString("endpoint.nio.registerFail"), x);
}
} else {
......
}
if (running && !paused && eventCache != null) {
pe.reset();
eventCache.push(pe);
}
}
return result;
}
event()方法逻辑大致如下:
(1). 从PollerEvent中得到NioSocketWrapper
(2). 从NioSocketWrapper得到SocketChannel
(3). 根据事件操作类型进行逻辑判断,最终把NioSocketWrapper注册到Selector中,真实位置把NioSocketWrapper赋值给了Selector的抽象子类SelectorImpl中的属性SelectionKey中的属性attachment
3.2 NioEndpoint$Poller.processKey(SelectionKey sk, NioSocketWrapper socketWrapper)
processKey:879, NioEndpoint$Poller{org.apache.tomcat.util.net}
protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {
......
if (socketWrapper.readOperation != null) {
if (!socketWrapper.readOperation.process()) {
closeSocket = true;
}
} else if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
closeSocket = true;
}
......
}
processKey()方法执行逻辑如下:
(1). 经过一系列判断,走到了processSocket()方法,执行成功后把closeSocket设置成true
3.3 AbstractEndpoint.processSocket()
processSocket:1165, AbstractEndpoint{org.apache.tomcat.util.net}
/**
* Process the given SocketWrapper with the given status. Used to trigger
* processing as if the Poller (for those endpoints that have one)
* selected the socket.
*
* @param socketWrapper The socket wrapper to process
* @param event The socket event to be processed
* @param dispatch Should the processing be performed on a new
* container thread
*
* @return if processing was triggered successfully
*/
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
SocketProcessorBase<S> sc = null;
if (processorCache != null) {
// 如果缓存栈里面有,从栈中弹出来
sc = processorCache.pop();
}
if (sc == null) {
// SocketProcessorBase实现了Runnable接口
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
// 获取线程执行器
Executor executor = getExecutor();
if (dispatch && executor != null) {
// 最终每一个Socket都交给了一个线程去执行处理
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;
}
processSocket()方法逻辑大致如下:
(1). 首先从缓存同步栈中获取执行器SocketProcessorBase(实现了Runnable),如果不存在创建SocketProcessorBase。
(2). 获取Tomcat容器启动时,NioEnpoint组件创建的线程池。
(3). 把SocketProcessorBase线程交给线程池执行。
3.3.1 AbstractEndpoint.createExecutor()
public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}
Tomcat容器一键式启动,在启动Connector连接器时,会先去启动Endpoint,NioEndpoint在启动时初始化了线程池。
createExecutor() 中创建的线程池默认参数如下:
corePoolSize(核心线程数): 10
maximumPoolSize(最大线程数): 200
keepAliveTime(线程存活时间): 60s
TimeUnit(时间单位):秒
BlockingQueue(阻塞队列):LinkedBlockingQueue
ThreadFactory(线程工厂): 自定义了线程工厂 new TaskThreadFactory(getName() + “-exec-”, daemon, getThreadPriority());
RejectedExecutionHandler(拒绝策略): throw new RejectedExecutionException();