tomcat nio Acceptor源码分析

acceptor线程监听端口,接收客户端的连接请求,将客户端连接交给poller处理
acceptor线程主要处理了三件事情:

  • 处理acceptor线程(自己这个线程)的暂停和停止
  • 接收客户端连接
  • 设置socket并提交到poller

Acceptor线程主要代码如下

    public void run() {

        int errorDelay = 0;
        long pauseStart = 0;

        try {
            while (!stopCalled) {
                // 1、处理暂停逻辑
                while (endpoint.isPaused() && !stopCalled) {
                    if (state != AcceptorState.PAUSED) {
                        pauseStart = System.nanoTime();
                        // Entered pause state
                        state = AcceptorState.PAUSED;
                    }
                    if ((System.nanoTime() - pauseStart) > 1_000_000) {
                        // Paused for more than 1ms
                        try {
                            if ((System.nanoTime() - pauseStart) > 10_000_000) {
                                Thread.sleep(10);
                            } else {
                                Thread.sleep(1);
                            }
                        } catch (InterruptedException e) {
                            // Ignore
                        }
                    }
                }

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

                try {
                    endpoint.countUpOrAwaitConnection();
                    if (endpoint.isPaused()) {
                        continue;
                    }

                    U socket = null;
                    try {
                        // 2、接收客户端连接
                        socket = endpoint.serverSocketAccept();
                    } catch (Exception ioe) {
                        endpoint.countDownConnection();
                        if (endpoint.isRunning()) {
                            errorDelay = handleExceptionWithDelay(errorDelay);
                            // re-throw
                            throw ioe;
                        } else {
                            break;
                        }
                    }
                    
                    errorDelay = 0;

                    if (!stopCalled && !endpoint.isPaused()) {
                        // 3、如果正常接收到客户端连接,通过endpoint#setSocketOptions方法处理客户端连接。其实就是交给Poller线程
                        if (!endpoint.setSocketOptions(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 org.apache.tomcat.jni.Error) {
                        org.apache.tomcat.jni.Error e = (org.apache.tomcat.jni.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线程通过while询盘判断外部是否调用了停止,如果没有则不断循环。循环内部处理了三件事情
1、处理暂停和停止
外部调用了暂停之后,Acceptor线程进入sleep,外部线程调用resume恢复Acceptor或者调用stop停止Acceptor,都只是设置了状态,Acceptor怎么才能知道状态改变了呢?那么就需要sleep一段时间后恢复过来轮询状态位,Acceptor设置了三个级别的轮询

  • 暂停1ms以内,直接忙等轮询
  • 暂停1ms到10ms之间,每次sleep 1ms轮询一次
  • 暂停10ms以上,每次sleep 10ms轮询1次

2、接收客户端连接
通过endpoint#serverSocketAccept方法接收客户端连接。NioEndpoint#serverSocketAccept直接调用serverSock#accept接收客户端连接

    protected SocketChannel serverSocketAccept() throws Exception {
        SocketChannel result = serverSock.accept();

        // Bug does not affect Windows. Skip the check on that platform.
        if (!JrePlatform.IS_WINDOWS) {
            SocketAddress currentRemoteAddress = result.getRemoteAddress();
            long currentNanoTime = System.nanoTime();
            if (currentRemoteAddress.equals(previousAcceptedSocketRemoteAddress) &&
                    currentNanoTime - previousAcceptedSocketNanoTime < 1000) {
                throw new IOException(sm.getString("endpoint.err.duplicateAccept"));
            }
            previousAcceptedSocketRemoteAddress = currentRemoteAddress;
            previousAcceptedSocketNanoTime = currentNanoTime;
        }

        return result;
    }

NioEndpoint启动时,把ServerSocket配置为阻塞模式(并没有利用Selector机制),所以NioEndpoint#serverSocketAccept方法会一直阻塞直到接收到客户端连接。由于Acceptor只会监听一个端口,所以阻塞在这里并没有什么问题

    protected void initServerSocket() throws Exception {
        if (getUseInheritedChannel()) {
            Channel ic = System.inheritedChannel();
            if (ic instanceof ServerSocketChannel) {
                serverSock = (ServerSocketChannel) ic;
            }
            if (serverSock == null) {
                throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
            }
        } else {
            serverSock = ServerSocketChannel.open();
            socketProperties.setProperties(serverSock.socket());
            InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
            serverSock.socket().bind(addr,getAcceptCount());
        }

    	// 将serverSock设置成阻塞模式
        serverSock.configureBlocking(true); //mimic APR behavior
    }

3、设置socket并提交到poller
通过NioEndpoint#setSocketOptions来处理客户端连接,这里会将SocketChannel包装成NioSocketWrapper,提交给Poller

    protected boolean setSocketOptions(SocketChannel socket) {
        NioSocketWrapper socketWrapper = null;
        try {
            // NioChannel是可以复用的,如果缓存没有则创建一个
            NioChannel channel = null;
            if (nioChannels != null) {
                channel = nioChannels.pop();
            }
            if (channel == null) {
                SocketBufferHandler bufhandler = new SocketBufferHandler(
                        socketProperties.getAppReadBufSize(),
                        socketProperties.getAppWriteBufSize(),
                        socketProperties.getDirectBuffer());
                if (isSSLEnabled()) {
                    channel = new SecureNioChannel(bufhandler, this);
                } else {
                    channel = new NioChannel(bufhandler);
                }
            }

            // 创建NioSocketWrapper
            NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this);
            channel.reset(socket, newWrapper);
            connections.put(socket, newWrapper);
            socketWrapper = newWrapper;

            // 设置socket属性,将SocketChannel配置为非阻塞模式
            socket.configureBlocking(false);
            socketProperties.setProperties(socket.socket());

            socketWrapper.setReadTimeout(getConnectionTimeout());
            socketWrapper.setWriteTimeout(getConnectionTimeout());
            socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());

            // 最后提交给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);
            }
        }
        return false;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值