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();

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值