Tomcat处理请求的线程模型详解

一、前言

JAVA后端项目,运行在容器tomcat中,由于现在springboot的内置tomcat容器,其默认配置屏蔽了很多对tomcat的认知,但是对tomcat的学习和认识是比较重要的,所以专门查资料加深了理解,本文主要讨论在springboot集成下的tomcat9的请求过程,线程模型为NIO。

二、tomcat结构

Tomcat处理请求的线程模型详解_第1张图片

找了张结构图,每个模块的意思和作用就不详解了,可以搜其他文章

三、探讨tomcat是如何处理请求

Tomcat处理请求的线程模型详解_第2张图片

自己画了一个connector的结构

1、初始化

在springboot启动后,org.springframework.context.support.AbstractApplicationContext#finishRefresh,这里进去调用org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start()方法启动TomcatWebServer,初始化tomcat。

Tomcat处理请求的线程模型详解_第3张图片

Tomcat处理请求的线程模型详解_第4张图片

通过这样的调用链到达org.apache.tomcat.util.net.NioEndpoint#startInternal(),进行初始化Endpoint中的AcceptorPoller,这两者都实现了Runnable接口,初始化后就通过线程start启动了。

2、如何处理客户端请求

Acceptor: 接收器,作用是接受scoket网络请求,并调用setSocketOptions()封装成为NioSocketWrapper,并注册到Poller的events中。注意查看run方法org.apache.tomcat.util.net.Acceptor#run

 @Override
    public void run() {
        int errorDelay = 0;
        try {
            // Loop until we receive a shutdown command
            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
                    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 {
                        // 等待下一个请求进来
                        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()) {
                        // 注册socket到Poller,生成PollerEvent事件
                        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 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;
    }

Poller:轮询器,轮询是否有事件达到,有请求事件到达后,以NIO的处理方式,查询Selector取出所有请求,遍历每个请求的需求,分配给Executor线程池执行。查看org.apache.tomcat.util.net.NioEndpoint.Poller#run()

 public void run() {
            // Loop until destroy() is called
            while (true) {
                boolean hasEvents = false;
                try {
                    if (!close) {
                        hasEvents = events();
                        if (wakeupCounter.getAndSet(-1) > 0) {
                            // If we are here, means we have other stuff to do
                            // Do a non blocking select
                            keyCount = selector.selectNow();
                        } else {
                            keyCount = selector.select(selectorTimeout);
                        }
                        wakeupCounter.set(0);
                    }
                    if (close) {
                        events();
                        timeout(0, false);
                        try {
                            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;
                }
				//查询selector取出所有请求
                Iterator iterator =
                    keyCount > 0 ? selector.selectedKeys().iterator() : null;
                // Walk through the collection of ready keys and dispatch
                // any active event.
                while (iterator != null && iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    iterator.remove();
                    NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
                    //处理请求key
                    if (socketWrapper != null) {
                        processKey(sk, socketWrapper);
                    }
                }
                // Process timeouts
                timeout(keyCount,hasEvents);
            }
            getStopLatch().countDown();
        }

请求过程大致如下图:

Tomcat处理请求的线程模型详解_第5张图片

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!  

你可能感兴趣的:(Tomcat处理请求的线程模型详解)