Tomcat - Tomcat 網絡通信模型剖析 & 併發參數解讀

在這裏插入圖片描述


什麼是IO

IO是指爲數據傳輸所提供的輸入輸出流,其輸入輸出對象可以是:文件、網絡服務、內存等。

在這裏插入圖片描述

舉個例子,假設應用在從硬盤中讀取一個大文件過程中, CPU會與硬盤一樣出於高負荷狀態麼?

CPU 沒有太高的增漲 。 通常情況下IO操作是比較耗時的,所以爲了高效的使用硬件,應用程序可以用一個專門線程進行IO操作,而另外一個線程則利用CPU的空閒去做其它計算。這種爲提高應用執行效率而採用的IO操作方法即爲IO模型。


Tomcat 支持四種線程模型

IO模型 描述
BIO 同步阻塞式IO,即Tomcat使用傳統的java.io進行操作。該模式下每個請求都會創建一個線程,對性能開銷大,不適合高併發場景。優點是穩定,適合連接數目小且固定架構。
NIO 同步非阻塞式IO,jdk1.4 之後實現的新IO。該模式基於多路複用選擇器監測連接狀態在同步通知線程處理,從而達到非阻塞的目的。比傳統BIO能更好的支持併發性能。Tomcat 8.0之後默認採用該模式
APR 全稱是 Apache Portable Runtime/Apache可移植運行庫),是Apache HTTP服務器的支持庫。可以簡單地理解爲,Tomcat將以JNI的形式調用Apache HTTP服務器的核心動態鏈接庫來處理文件讀取或網絡傳輸操作。使用需要編譯安裝APR 庫
AIO (asynchronous I/O) 異步非阻塞式IO,jdk1.7後之支持 。與nio不同在於不需要多路複用選擇器,而是請求處理線程執行完程進行回調調知,已繼續執行後續操作。Tomcat 8之後支持。

Tomcat 如何使用指定IO模型

配置 server.xml 文件當中的 <Connector protocol="HTTP/1.1"> 修改即可。

默認配置 8.0 protocol=“HTTP/1.1” 8.0 之前是 BIO 8.0 之後是NIO

BIO: protocol ="org.apache.coyote.http11.Http11Protocol"

NIO: protocol ="org.apache.coyote.http11.Http11NioProtocol"

AIO: protocol ="org.apache.coyote.http11.Http11Nio2Protocol"

APR: protocol ="org.apache.coyote.http11.Http11AprProtocol"

tomcat 8 以前 默認的是 BIO , 8 默認是 NIO


Tomcat BIO VS NIO

在高併發場景下BIO與NIO的線程數的變化

在這裏插入圖片描述

生產環境上,模擬了處理較慢的過程。

爲什麼差異這麼大呢?

我們先看下這兩種模型的區別

BIO

在這裏插入圖片描述

源碼翻一翻

我們下個Tomcat 7的版本

https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.99/src/

  <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

7嘛 ,默認的BIO, 對應的class org.apache.coyote.http11.Http11Protocol.java

看下 Http11Protocol構造函數

    public Http11Protocol() {
        endpoint = new JIoEndpoint();
        cHandler = new Http11ConnectionHandler(this);
        ((JIoEndpoint) endpoint).setHandler(cHandler);
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }

重點 JIoEndpoint ------> Java I/O Endpoint

在這裏插入圖片描述

對應JIoEndpoint 中的

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

熟悉吧 Socket, 同步阻塞。

繼續
在這裏插入圖片描述

在這裏插入圖片描述

這個getExecutor()獲取的 就是 那個線程池,用來處理任務的

怎麼處理的呢? 繼續

在這裏插入圖片描述


NIO

在這裏插入圖片描述

老套路

下載 apache-tomcat-8.5.55-src

Http11NioProtocol的構造函數
在這裏插入圖片描述

重點 NioEndpoint

在這裏插入圖片描述

Acceptor 和 SocketProcessor 大致形同, 主要在於Poller .

先看看Acceptor ,和BIO一樣 ,有點不一樣的是,Acceptor 交個 Poller處理

在這裏插入圖片描述
進入

在這裏插入圖片描述
添加事件
在這裏插入圖片描述


 public class Poller implements Runnable { 
 

} 

那就看run方法

public void run() {
            // Loop until destroy() is called  輪詢
            while (true) {

                boolean hasEvents = false;

                try {
                    if (!close) {
                        hasEvents = events();
                        // > 0 說明有socket需要處理
                        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;
                    }
                } catch (Throwable x) {
                    ExceptionUtils.handleThrowable(x);
                    log.error("",x);
                    continue;
                }
                //either we timed out or we woke up, process events first
                if ( keyCount == 0 ) hasEvents = (hasEvents | events());

                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();
                    NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
                    // Attachment may be null if another thread has called
                    // cancelledKey()
                    if (attachment == null) {
                        iterator.remove();
                    } else {
                        iterator.remove();
                        processKey(sk, attachment);
                    }
                }//while

                //process timeouts
                timeout(keyCount,hasEvents);
            }//while

            getStopLatch().countDown();
        }

影響 BIO/NIO線程數量的多少的因素

BIO

  • 線程數量 會受到 客戶端阻塞、網絡延遲、業務處理慢===>線程數會更多

NIO

  • 線程數量 會受到業務處理慢===>線程數會更多

Tomcat connector 併發參數解讀

名稱 描述
acceptCount 等待最大隊列
address 綁定客戶端特定地址,127.0.0.1
bufferSize 每個請求的緩衝區大小。bufferSize * maxThreads
compression 是否啓用文檔壓縮
compressableMimeTypes text/html,text/xml,text/plain
connectionTimeout 客戶發起鏈接 到 服務端接收爲止,中間最大的等待時間
connectionUploadTimeout upload 情況下連接超時時間
disableUploadTimeout true 則使用connectionTimeout
enableLookups 禁用DNS查詢 true
keepAliveTimeout 當長鏈接閒置 指定時間主動關閉 鏈接 ,前提是客戶端請求頭 帶上這個 head"connection" " keep-alive"
maxKeepAliveRequests 最大的 長連接數
maxHttpHeaderSize HTTP請求、響應頭信息的最大大小,默認是8192bytes
maxSpareThreads BIO 模式下 最多線閒置線程數
maxThreads (執行線程) 最大執行線程數
minSpareThreads (初始線業務線程 10) BIO 模式下 最小線閒置線程數
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章