溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Tomcat處理請求的線程模型是什么

發(fā)布時間:2022-03-25 09:17:17 來源:億速云 閱讀:133 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下Tomcat處理請求的線程模型是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

    一、前言

    JAVA后端項目,運行在容器tomcat中,由于現(xiàn)在springboot的內(nèi)置tomcat容器,其默認(rèn)配置屏蔽了很多對tomcat的認(rèn)知,但是對tomcat的學(xué)習(xí)和認(rèn)識是比較重要的,所以專門查資料加深了理解,本文主要討論在springboot集成下的tomcat9的請求過程,線程模型為NIO。

    二、tomcat結(jié)構(gòu)

    Tomcat處理請求的線程模型是什么

    找了張結(jié)構(gòu)圖,每個模塊的意思和作用就不詳解了,可以搜其他文章

    三、探討tomcat是如何處理請求

    Tomcat處理請求的線程模型是什么

    自己畫了一個connector的結(jié)構(gòu)

    1、初始化

    在springboot啟動后,org.springframework.context.support.AbstractApplicationContext#finishRefresh,這里進(jìn)去調(diào)用org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start()方法啟動TomcatWebServer,初始化tomcat。

    Tomcat處理請求的線程模型是什么

    Tomcat處理請求的線程模型是什么

    通過這樣的調(diào)用鏈到達(dá)org.apache.tomcat.util.net.NioEndpoint#startInternal(),進(jìn)行初始化Endpoint中的AcceptorPoller,這兩者都實現(xiàn)了Runnable接口,初始化后就通過線程start啟動了。

    2、如何處理客戶端請求

    Acceptor: 接收器,作用是接受scoket網(wǎng)絡(luò)請求,并調(diào)用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 {
                            // 等待下一個請求進(jìn)來
                            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:輪詢器,輪詢是否有事件達(dá)到,有請求事件到達(dá)后,以NIO的處理方式,查詢Selector取出所有請求,遍歷每個請求的需求,分配給Executor線程池執(zhí)行。查看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<SelectionKey> 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處理請求的線程模型是什么

    以上是“Tomcat處理請求的線程模型是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI