Tomcat中Acceptor线程run方法工作流程

1. Acceptor线程run方法工作流程概述:

(1). 把Socket请求封装成一个PollerEvent事件
(2). 添加到Poller线程中的同步队列SynchronizedQueue中,使用了典型的生产者-消费者模式,通过共享资源实现线程之间的通讯。

2. Acceptor线程什么启动?

Acceptor启动调用链如下:
在这里插入图片描述
Tomcat容器一键式启动,在启动Connector连接器时,会先去启动Endpoint,NioEndpoint.startInternal()方法调用AbstractEndpoint.startAcceptorThread()方法,触发Acceptor线程的start()方法,Acceptor线程启动。

startAcceptorThread:1304, AbstractEndpoint{org.apache.tomcat.util.net}代码具体如下:

    protected void startAcceptorThread() {
        acceptor = new Acceptor<>(this);
        String threadName = getName() + "-Acceptor";
        acceptor.setThreadName(threadName);
        Thread t = new Thread(acceptor, threadName);
        // NORM_PRIORITY = 5;Thread默认提供了3种线程优先级(1、5、10),Acceptor使用正常线程优先级:5
        t.setPriority(getAcceptorThreadPriority());
        // daemon = true;设置Acceptor线程为守护线程
        t.setDaemon(getDaemon());
        t.start();
    }

3. Acceptor.run()方法调用链源码跟踪

调用链接如下:
在这里插入图片描述

3.1 Acceptor.run()

run:126, Acceptor{org.apache.tomcat.util.net}代码如下:

@Override
public void run() {

    int errorDelay = 0;

    try {
        // Loop until we receive a shutdown command
        // 该线程会一直进行while循环,除非收到了shutdown命令,
        // 但实际上由于下面ServerSocketChannel默认构造是blocking模式,accept()方法对于blocking模式的channel,
        // 策略:一直阻塞,直到可以获得一个新的连接,或者发生了I/O错误。
        // 所以当没有连接请求来时,线程会堵塞在accept方法上。
        while (!stopCalled) {

            // Loop if endpoint is paused
            while (endpoint.isPaused() && !stopCalled) {
                state = AcceptorState.PAUSED;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // Ignore
                }
            }

            if (stopCalled) {
                break;
            }
            state = AcceptorState.RUNNING;

            try {
                // if we have reached max connections, wait
                // 当有新的请求到达时,endpint使用自定义的org.apache.tomcat.util.threads.LimitLatch来管理连接数
                // 如果连接数大于最大值,等待,否则LimitLatch的count值加1。最大值为:maxConnections = 8*1024;
                endpoint.countUpOrAwaitConnection();

                // Endpoint might have been paused while waiting for latch
                // If that is the case, don't accept new connections
                if (endpoint.isPaused()) {
                    continue;
                }

                U socket = null;
                try {
                    // Accept the next incoming connection from the server socket
                    // 从server socket接受下一次进来的连接
                    // serverSocketAccept()方法逻辑是通过ServerSocketChannel.accept()监听连接请求,
                    // 由于ServerSocketChannel使用blocking模式,所以当没有连接请求来时,线程会堵塞在accept方法上。
                    socket = endpoint.serverSocketAccept();
                } catch (Exception ioe) {
                    // We didn't get a socket
                    endpoint.countDownConnection();
                    if (endpoint.isRunning()) {
                        // Introduce delay if necessary
                        errorDelay = handleExceptionWithDelay(errorDelay);
                        // re-throw
                        throw ioe;
                    } else {
                        break;
                    }
                }
                // Successful accept, reset the error delay
                errorDelay = 0;

                // Configure the socket
                if (!stopCalled && !endpoint.isPaused()) {
                    // setSocketOptions() will hand the socket off to an appropriate processor if successful
                    // setSocketOptions()方法如果调用成功了,会把socket传递给合适的processor
                    if (!endpoint.setSocketOptions(socket)) {
                    	// 传递完之后endpint,关闭socket
                        endpoint.closeSocket(socket);
                    }
                } else {
                    endpoint.destroySocket(socket);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                String msg = sm.getString("endpoint.accept.fail");
                // APR specific.
                // Could push this down but not sure it is worth the trouble.
                if (t instanceof Error) {
                    Error e = (Error) t;
                    if (e.getError() == 233) {
                        // Not an error on HP-UX so log as a warning
                        // so it can be filtered out on that platform
                        // See bug 50273
                        log.warn(msg, t);
                    } else {
                        log.error(msg, t);
                    }
                } else {
                        log.error(msg, t);
                }
            }
        }
    } finally {
        stopLatch.countDown();
    }
    state = AcceptorState.ENDED;
}

Acceptor类实现了Runnable接口,重写了run方法,方法逻辑大致如下:
1). while循环对stopCalled值做判断。
2). 当有新的请求到达时,endpint使用自定义的org.apache.tomcat.util.threads.LimitLatch来管理连接数,如果连接数大于最大值,等待,否则LimitLatch的count值加1。
3). 通过ServerSocketChannel.accept()监听连接请求,当没有连接请求来时,线程会堵塞在accept方法上。
4). 调用setSocketOptions()方法把socket传递给合适的processor。

3.2 NioEndpoint.setSocketOptions(SocketChannel socket)

setSocketOptions:535, NioEndpoint{org.apache.tomcat.util.net}代码如下:

/**
 * Process the specified connection.
 * 执行指定的连接
 * @param socket The socket channel
 * @return <code>true</code> if the socket was correctly configured
 *  and processing may continue, <code>false</code> if the socket needs to be
 *  close immediately
 */
@Override
protected boolean setSocketOptions(SocketChannel socket) {
    NioSocketWrapper socketWrapper = null;
    try {
        // Allocate channel and wrapper
        // 初始化channel和wrapper,填充属性,实例化,poller线程后面会处理
        NioChannel channel = null;
        if (nioChannels != null) {
            // private SynchronizedStack<NioChannel> nioChannels; 从同步栈中弹出一个channel
            channel = nioChannels.pop();
        }
        if (channel == null) {
        	// 预分配读缓冲区、写缓冲区、直接缓冲区
            SocketBufferHandler bufhandler = new SocketBufferHandler(
                    socketProperties.getAppReadBufSize(),
                    socketProperties.getAppWriteBufSize(),
                    socketProperties.getDirectBuffer());
            if (isSSLEnabled()) {
                channel = new SecureNioChannel(bufhandler, selectorPool, this);
            } else {
            	// 构造NioChannel实例,把SocketBufferHandler赋值给NioChannel属性
                channel = new NioChannel(bufhandler);
            }
        }
        // 构造NioSocketWrapper
        NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this);
        // 把socket赋值给构造的NioChannnel中SocketChannel
        channel.reset(socket, newWrapper);
        // map映射
        connections.put(socket, newWrapper);
        socketWrapper = newWrapper;

        // Set socket properties
        // Disable blocking, polling will be used
        // socket配置成阻塞模式
        socket.configureBlocking(false);
        if (getUnixDomainSocketPath() == null) {
            socketProperties.setProperties(socket.socket());
        }

        socketWrapper.setReadTimeout(getConnectionTimeout());
        socketWrapper.setWriteTimeout(getConnectionTimeout());
        socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
        // 把封装好的NioSocketWrapper注册到Poller线程中同步队列中
        poller.register(socketWrapper);
        return true;
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        try {
            log.error(sm.getString("endpoint.socketOptionsError"), t);
        } catch (Throwable tt) {
            ExceptionUtils.handleThrowable(tt);
        }
        if (socketWrapper == null) {
            destroySocket(socket);
        }
    }
    // Tell to close the socket if needed
    return false;
}

NioSocketWrapper() 方法逻辑大致如下:
(1). 初始化channel和wrapper,填充属性,对读缓冲区、写缓冲区、直接缓冲区进行预分配,进行实例化
(2). 把SocketChannel信息和预分配的数据,封装到NioSocketWrapper对象中
(3). 调用Poller线程提供的register方法

3.3 NioEndpoint$Poller.register(final NioSocketWrapper socketWrapper)

register: 771, NioEndpoint$Poller{org.apache.tomcat.util.net}代码如下:

/**
 * Registers a newly created socket with the poller.
 *
 * @param socketWrapper The socket wrapper
 */
public void register(final NioSocketWrapper socketWrapper) {
    socketWrapper.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
    PollerEvent event = null;
    if (eventCache != null) {
        event = eventCache.pop();
    }
    if (event == null) {
        event = new PollerEvent(socketWrapper, OP_REGISTER);
    } else {
        event.reset(socketWrapper, OP_REGISTER);
    }
    addEvent(event);
}

register()方法如下:
(1). 把NioSocketWrapper封装到PollerEvent对象中
(2). 调用Poller线程提供的addEvent方法

3.4 NioEndpoint$Poller.addEvent(PollerEvent event)

addEvent:667,NioEndpoint$Poller{org.apache.tomcat.util.net}代码如下:

private void addEvent(PollerEvent event) {
   events.offer(event);
    if (wakeupCounter.incrementAndGet() == 0) {
        selector.wakeup();
    }
}

addEvent()逻辑如下:
(1). 添加到PollerEvent中的同步队列顶部
(2). 如果selector数量为0,唤醒selector

4. 涉及到的其他类

SocketChannel

java.nio.channels.SocketChannel

ServerSocketChannel

java.nio.channels.ServerSocketChannel

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值