Springboot 內嵌Tomcat 的http連接池與thread的關系


前言


  • 最近看了很多tcp/ip 連接以及 IO相關的文章,但是依舊對數據庫連接池等的部分不是很清楚, 所以這里僅是簡單描述一下tomcat對應的http連接池數量的情況,不考慮與數據庫的連接池的情況.

Linux內核相關


  • 周天時簡單總結了下linux內核的兩個參數.somaxconn以及tcp_max_sync_backlog.這兩天又學習了解了下.發現這兩個配置文件其實講的是全連接以及半連接的隊列, 但是真正開始進入數據傳輸時應該就已經不在這兩個隊列里面了. 如下圖所示.
    image
  • 所以前面兩個參數應該是負責處理連接隊列的,整整進入tomcat與client的交互時應該就不需要使用者兩個隊列了.核心就會變成其他的參數.
  • 這里我理解核心的參數應該是 1. 當前操作系統剩余內存能夠支撐的tcp連接數, 2.nofile 當前進程能夠打開的文件數減去程序已經加載的文件數. 比如我們springboot的項目如果有1000個jar包可能就會產生1000-2000個fd的連接數,加上日志,以及一些監聽pid等文件nofile -2000 左右才是可以使用的tcp連接數
  • 這里 我的理解不一定正確, 我認為一個與tomcat 進行的tcp連接至少要占用一個文件描述符, 但是不清楚一個tcp連接復用多個http連接會不會占用更多的fd.

Tomcat相關


  • 還是回到之前的配置參數
server:
  #tomcat配置
  tomcat:
    # 當所有線程都在使用時,建立連接的請求的等待隊列長度,默認100
    accept-count: 1000
    # 線程池維持最小線程數,默認10
    min-spare-threads: 4
    # 允許最大連接數,默認10000,當達到臨界值時,系統可能會基於accept-count繼續接受連接
    max-connections: 10000
    # 最大線程數,默認200
    max-threads: 1000
  • 這里進行解釋:
max-thread:  應該就是 jstack-l 里面出現的 http 開頭的thread信息, 隨着threads 的數量的增加, 能夠創建的線程會更多, 但是線程的創建和切換都是有成本的, 雖然線程能夠共享進程的內存以及部分寄存器,但是上下文還是需要每次切換時進行switch,  線程多了切換的次數會變多,並且管理成本也比較高, 不建議設置的特別多,除非操作系統的cores 數特別多. 
min-spare-threads: 這個參數可以應付在冷啟動之后突然爆發的大量請求,避免一開會無法批量創建threads 時造成影響非常緩慢,建議可以保留一些. 

需要注意的一點是 這兩個線程都是 jvm 內部的線程, 內個現場都需要有 java的stack的size. 理論上默認值是 1m 左右, 可以設置為 256k 或者是更小 但是不能低於 172KB 還是一個具體的多大的數值, 但是如果棧空間過小, 會出現stack over flow的錯誤,並且遞歸的深度可能會不夠, 不建議將棧空間設置的過小,畢竟會導致棧幀數量下降. 

需要注意的另外一點,除非tomcat的http線程 還需要有類似於1. redis的連接線程. 2.gc線程. 3.數據庫連接線程. 但是數據庫的連接池好像不是在jvm的堆區和棧區管理的, 這一塊需要找專家同事學習溝通一下. 我還沒看到那一塊內容. 

max-connections: 關於這個參數,可能需要專門說明一下, 因為linux有BIO NIO以及AIO等IO模型,一般的說法是,自從tomcat 7 之后已經不支持BIO模型了,BIO時 沒一個線程只能為一個 connection服務, 會導致吞吐量下降的很厲害. NIO非阻塞的IO時, 因為在內核和網卡驅動這一塊時是不需要thread等待的, 所以thread可以為其他進程服務, 所以這個時候thread可以為遠遠大於自己的connections 來提供服務, 但是需要注意的一點是,如果是高CPU 低IO的場景, connection 是thread 的比較高的倍數時就顯得不太合適了, 只有IO占用較多資源和時間時可以將connection 數值設置的thread大很多, 默認值就是10000. 

關於這個10000 我這里的理解時 如果沒個connection的round time 都是10s 左右的話如果每個線程只需要有1s左右的線程計算時間,那么理論上1000個thread就可以提供服務了. 但是具體的算法需要參照源碼,可能比我想象的要復雜很多. 

accept-count:  這個參數就會到了咱們內核參數里面的 somaxconn 參數對應了,就算這個參數可以設置的很大, 比如1000 但是實際上 具體的連接池大小是收到somaxconn和accept-count里面的較小值來決定的, 所以如果線程數較少,並且並發數產生了很多的連接數時很容易就會將max-connections 的數值打滿,然后開始用accept-count的參數值,如果somaxconn是默認值的話 比如centos7 上面都是 128 那么第 10129個連接應該直接就會被linux 給drop 客戶端返回connection reset 的提示信息 (自己推斷的,還沒明確實驗)

IO相關


  • 最近有很多公眾號在將linux的IO模型,最近一個月以來也看了很多內容,理解的不是很透徹, 這里簡單總結一下.
IO有很多種類,磁盤是IO,鍵盤是IO,網絡棧的收發其實也是IO操作.
因為CPU都是納秒級別的反應, 網絡的IO都是幾十上百毫秒的延遲, 這里面有六七個數量級的延遲,為了不讓CPU干耗着去等待緩慢的網絡IO(機械磁盤IO也是如此),在最開始的blocked IO 基礎之上 慢慢研發了NIO和AIO. 
需要注意的是linux比較多的是NIO noblocked 但是AIO 也就是異步非阻塞IO實現的比較少一些. 
阻塞和非阻塞IO的核心區別在於 在CPU獲取到需要的IO數據之前 用戶態的線程是在等待還是在干別的
如果在等待,那么就是阻塞IO,如果不等待而去干別的了 就是非阻塞IO.
CPU獲取到數據之后,如果從內核態往用戶態傳輸數據時是需要用戶態線程參與的,就還是同步的
如果只是CPU將需要的IO數據復制到了用戶態需要使用的地方,直接通知用戶線程進行處理,那么就是異步的. 

需要注意的是 linux還有一個zero copy的概念, 其實與內存里面的 copy on write 類似
都是避免不必要的內存讀寫,節約寶貴的總線帶寬或者是內存帶寬拉實現提高性能的操作. 

nginx 就使用了sendfile的方式進行 zero copy的機制來提高靜態文件的傳輸速度, 記得能夠減少一次內核態到用戶態的文件copy,只是傳遞fd就可以讓網卡直接傳輸client想服務器申請訪問的資源.

tomcat 作為 app server 感覺這一塊不如 nginx這種io復用的純web server 處理靜態資源來的省時省力. 
所以tomcat有一個handler 可以進行判斷請求類型,如果是靜態類型的話 交由一個類似的nginx web server 來處理,但是還是感覺會導致tomcat的負載變大, 建議還是前端靜態文件還是專門有一個靜態文件服務器來跑比較好一些. 

疑惑的地方


  • springboot 開起來的進程之后線程數其實非常多,有redis的,有操作連接池的,也有tomcat用來實現http連接等的. 還有跟當前CPU數目一致數量的GC線程. 但是我一直不知道數據庫連接池是如何管理的,雖然能在jstack 打開的thread 信息里面看到hikari pool 等的相關信息.但是感覺這些只是springboot 進行使用數據庫連接池的進程,而不是管理數據庫連接池的線程.
  • dump堆棧信息里面包含的內容,是否不包含native memory和method pool等. 以及連接池到底存放在何處,如何進行監控,如果單一個db多個app-server時 如果每一個app-server都保持一個比較多的數據量連接池,那么會不會對數據庫服務器有較大的壓力存在?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM