引言
寫這篇文章的起因源於筆者對一個小問題的不斷追溯,並非是先去看類似的文章然後總結得到,這樣做的好處就是在於具有目的性,找答案時也會更有興趣。這個小問題大家平時在springMVC專案中基本都會碰到:生產日誌down下來發現經常會有穿插列印兩個介面日誌的現象,經過分析發現第一個request執行到一部分還沒執行完時第二個request又上來了,因為2個request對應的介面都會呼叫一些其它系統或者建立,關閉sqlSession這類需要等待耗時的操作,在某個介面執行到這種等待期時,另一個介面就會在這個間隙繼續往下執行,最終造成日誌一會列印A介面的執行,一會列印B介面的執行。基於此現象,筆者產生了以下猜想。
正文
兩個介面的執行是否對應了2個執行緒?如果是這樣,是否意味著容器每收到一個請求都會建立1個執行緒?
有個顯而易見的東西,某個request沒執行完,也就是沒返回response前系統是可以處理其它請求的。
筆者想要確定出現這種情況時,是否是兩個不同的執行緒在分別跑兩個介面,想通過測試搞清楚,原理是在兩個介面執行過程中把執行緒地址打出來,通過比較是否一樣確定是否是2個執行緒,將原來的springMVC專案找到2個查詢介面。
在A介面中間某個程式段加上如下程式碼塊
logger.info("A1>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+Thread.currentThread());
try {
Thread.sleep(new Long(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("A2>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+Thread.currentThread());複製程式碼
在B介面中間某個程式段加上如下程式碼塊
logger.info("B1>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+Thread.currentThread());
try {
Thread.sleep(new Long(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("B2>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+Thread.currentThread());複製程式碼
然後寫了2個測試類,用來模擬http請求,測試類1向A介面發請求,測試類2向B介面發請求
使用tomcat啟動專案,然後執行測試類1,間隔3秒左右啟動測試類2,等待了幾秒鐘,測試類1,2都分別收到了回覆,檢視日誌,發現列印順序如下
......
A1>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Thread[http-apr-8090-exec-2,5,main]
......
B1>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Thread[http-apr-8090-exec-4,5,main]
......
A2>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Thread[http-apr-8090-exec-2,5,main]
......
B2>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Thread[http-apr-8090-exec-4,5,main]
......複製程式碼
由列印日誌不難發現,的確是2個執行緒
那麼能否據此推測說容器每接收到1個請求就會建立1個執行緒呢?答案是否定的
以tomcat為例,暫不考慮其它容器,經過大量的資料查詢,發現和tomcat的執行模式有關係,tomcat有3種執行模式,分別是
- bio模式
即每1個請求,tomcat都會建立1個執行緒,現在的tomcat基本都用執行緒池處理,類似資料庫的連線池,提前建立好一定數量的連線,比起請求來了再建立省很多時間。很容易想到,如果併發量很大時,就會需要很多執行緒,記憶體肯定很容易溢位的。tomcat配置檔案中的Connector節點下預設maxConnections=maxThreads,如果是bio模式,如果想要改變maxThreads(預設75),只需要配置maxThreads
- nio模式
maxConnections>maxThreads,即nio模式相對bio模式同等情況下減少了執行緒數,筆者的理解就是通過某種優化最大化壓榨CPU,把時間片都更好利用起來,這裡也說明了1個請求未必對應1個執行緒,之前做過netty專案其通訊就是nio模式,這裡附上1篇高質量講解NIO模式的文章:www.jianshu.com/p/76ff17bc6…,有興趣可以看下
- apr 模式
和nio模式類似,主要區別在於處理靜態資源的能力更強
如何檢視你的專案是什麼執行模式?
檢視啟動日誌,會發現類似這樣的東西:
資訊: Initializing ProtocolHandler ["http-nio-8090"]
四月 10, 2019 9:16:19 下午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
資訊: Using a shared selector for servlet write/read
四月 10, 2019 9:16:19 下午 org.apache.coyote.AbstractProtocol init
資訊: Initializing ProtocolHandler ["http-bio-8443"]
複製程式碼
從日誌看出,tomcat在8090埠以nio模式執行,8443埠以bio模式執行
3種執行模式的適用情況:
bio方式基本被淘汰了
nio方式適用於連線數多,連線時間短的架構,比如QQ,微信
apr方式使用於連線數多, 連線時間長的架構,比如相簿伺服器,QQ空間相簿
如果不指定,tomcat有預設的執行模式,7.0.30以後預設是apr模式
執行模式修改:
平時大家應該很少涉及,這篇文章主要是用來理解的,修改很簡單,大把教程,如有需要自行百度