文章目錄
什麼是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 模式下 最小線閒置線程數 |