tomcat请求处理-io交互过程解析

tomcat对请求进行处理分三个阶段,分别是io交互过程、数据加工过程、servlet处理过程,今天先看io过程。tomcat启动完成后会创建两个线程,一个是accept线程监听socket连接,一个是select线程轮询socket事件。

  • accept
    代码简化后如下,逻辑就是调用endpoint(NioEndpoint)serverSocketAccept()方法监听来自客户端的连接请求,如果有客户端请求服务器,则会先建立连接,此时会返回一个socket。然后setSocketOptions中进行处理。
    public void run() {

        try {
            while (!stopCalled) {

                .....

                try {

                    U socket = null;
                    try {
                        // 阻塞监听来自socket的连接
                        socket = endpoint.serverSocketAccept();
                    } catch (Exception ioe) {
                        ......
                    }
                    errorDelay = 0;

                    if (!stopCalled && !endpoint.isPaused()) {
                        //处理socket
                        if (!endpoint.setSocketOptions(socket)) {
                            endpoint.closeSocket(socket);
                        }
                    } else {
                        endpoint.destroySocket(socket);
                    }
                } catch (Throwable t) {
                    ......
                }
            }
        } finally {
            stopLatch.countDown();
        }
        state = AcceptorState.ENDED;
    }

处理的过程是创建NioSocketWrapper封装NioChannel、NioEndpoit、SocketChannel,将socket设置为非阻塞模式(read/write会立刻返回),使用内置的poller(Poller)进行注册。

    protected boolean setSocketOptions(SocketChannel socket) {
        NioSocketWrapper socketWrapper = null;
        try {
            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 newWrapper = new NioSocketWrapper(channel, this);
            channel.reset(socket, newWrapper);
            connections.put(socket, newWrapper);
            socketWrapper = newWrapper;

            socket.configureBlocking(false);
            if (getUnixDomainSocketPath() == null) {
                socketProperties.setProperties(socket.socket());
            }

            socketWrapper.setReadTimeout(getConnectionTimeout());
            socketWrapper.setWriteTimeout(getConnectionTimeout());
            socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
            poller.register(socketWrapper);
            return true;
        } catch (Throwable t) {
            ......
        }
        return false;
    }

注册到Poller的过程使用Java中生产、消费模式,先创建PollerEvent,再添加到eventsprivate final SynchronizedQueue<PollerEvent> events =new SynchronizedQueue<>();,然后唤醒selector,接下来看select线程表演。

        public void register(final NioSocketWrapper socketWrapper) {
            socketWrapper.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
            PollerEvent pollerEvent = createPollerEvent(socketWrapper, OP_REGISTER);
            addEvent(pollerEvent);
        }
        private void addEvent(PollerEvent event) {
            events.offer(event);
            if (wakeupCounter.incrementAndGet() == 0) {
                selector.wakeup();
            }
        }
  • select
    Poller的select线程现在还在调用select等待客户端的连接,收到wakeup()信号后,立刻被唤醒,然后到events()方法进行处理。events()做的事情就是注册socket的Read事件sc.register(getSelector(), SelectionKey.OP_READ, socketWrapper);,同事绑定封装的NioSocketWrapper到socket上面。
public void run() {
            while (true) {

                boolean hasEvents = false;

                try {
                    if (!close) {
                        hasEvents = events();
                        if (wakeupCounter.getAndSet(-1) > 0) {
                            keyCount = selector.selectNow();
                        } else {
                            keyCount = selector.select(selectorTimeout);
                        }
                        wakeupCounter.set(0);
                    }
                    ......
                    if (keyCount == 0) {
                        hasEvents = (hasEvents | events());
                    }
                } catch (Throwable x) {
                    ......
                }

                Iterator<SelectionKey> iterator =
                    keyCount > 0 ? selector.selectedKeys().iterator() : null;
                while (iterator != null && iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    iterator.remove();
                    NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
                    if (socketWrapper != null) {
                        processKey(sk, socketWrapper);
                    }
                }

                timeout(keyCount,hasEvents);
            }

            getStopLatch().countDown();
        }

此时第一遍循环结束,Poller的run开始第二次循环用于读取来自客户端发送的请求数据,当socket可读时,select返回可读的socket数量keyCountprivate AtomicLong wakeupCounter = new AtomicLong(0);,然后读取socket数据processKey(sk, socketWrapper);

                while (iterator != null && iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    iterator.remove();
                    NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
                    // Attachment may be null if another thread has called
                    // cancelledKey()
                    if (socketWrapper != null) {
                        processKey(sk, socketWrapper);
                    }
                }

接下来会创建SocketProcessor交由tomcat内置线程池进行异步处理,再往下就是读取客户端的数据加工成Request对象,这个后面再看。

    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) {
                sc = createSocketProcessor(socketWrapper, event);
            } else {
                sc.reset(socketWrapper, event);
            }
            Executor executor = getExecutor();
            if (dispatch && executor != null) {
                executor.execute(sc);
            } else {
                sc.run();
            }
        } ......
        return true;
    }
  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Tomcat源码解析是对Tomcat服务器的源代码进行分析和解读的过程。通过对Tomcat源码的研究,可以深入了解Tomcat的整体架构、连接器的内部结构、容器分析以及Tomcat的启动流程等方面的内容。 Tomcat的整体架构包括配置文件server.xml的分析和连接器的内部结构。配置文件server.xml是Tomcat的主要配置文件,通过对其进行分析可以了解Tomcat的各个组件和配置项的作用。连接器是Tomcat的核心组件之一,负责处理客户端请求并将其转发给相应的容器进行处理Tomcat的启动流程是通过实现Lifecycle接口的各个组件来完成的。在启动过程中,Tomcat会按照一定的顺序初始化和启动各个组件,确保它们能够正常工作。具体的启动流程可以通过阅读源码中的相关方法和注释来了解。 Tomcat底层使用了Netty来实现IO相关的操作,但与Netty有所区别,因为Tomcat对部分处理进行了封装。通过对Tomcat源码的学习,可以了解Tomcat底层的实现逻辑、各个组件的配合方式以及各种设计模式的交互。 如果你对Tomcat源码解析感兴趣,可以参考提供的源码和相关文章进行深入研究。通过深入研究Tomcat源码,你可以更好地理解Tomcat的工作原理和内部机制。 #### 引用[.reference_title] - *1* [Tomcat源码分析](https://blog.csdn.net/sun_code/article/details/123554480)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [tomcat线程模型-源码解析](https://blog.csdn.net/qq_16498553/article/details/126080174)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值