Tomcat 的連線數與執行緒池

2021-09-02 21:41:14 字數 3463 閱讀 1031

connector在處理http請求時,會使用不同的protocol。不同的tomcat版本支援的protocol不同,其中最典型的protocol包括bio、nio和apr(tomcat7中支援這3種,tomcat8增加了對nio2的支援,而到了tomcat8.5和tomcat9.0,則去掉了對bio的支援)。

bio是blocking io,顧名思義是阻塞的io;nio是non-blocking io,則是非阻塞的io。而apr是apache portable runtime,是apache可移植執行庫,利用本地庫可以實現高可擴充套件性、高效能;apr是在tomcat上執行高併發應用的首選模式,但是需要安裝apr、apr-utils、tomcat-native等包。

connector使用哪種protocol,可以通過元素中的protocol屬性進行指定,也可以使用預設值。

指定的protocol取值及對應的協議如下:

如果沒有指定protocol,則使用預設值http/1.1,其含義如下:在tomcat7中,自動選取使用bio或apr(如果找到apr需要的本地庫,則使用apr,否則使用bio);在tomcat8中,自動選取使用nio或apr(如果找到apr需要的本地庫,則使用apr,否則使用nio)。

無論是bio,還是nio,connector處理請求的大致流程是一樣的:

在accept佇列中接收連線(當客戶端向伺服器傳送請求時,如果客戶端與os完成三次握手建立了連線,則os將該連線放入accept佇列);在連線中獲取請求的資料,生成request;呼叫servlet容器處理請求;返回response。為了便於後面的說明,首先明確一下連線與請求的關係:連線是tcp層面的(傳輸層),對應socket;請求是http層面的(應用層),必須依賴於tcp的連線實現;乙個tcp連線中可能傳輸多個http請求。

在bio實現的connector中,處理請求的主要實體是jioendpoint物件。jioendpoint維護了acceptor和worker:acceptor接收socket,然後從worker執行緒池中找出空閒的執行緒處理socket,如果worker執行緒池沒有空閒執行緒,則acceptor將阻塞。其中worker是tomcat自帶的執行緒池,如果通過配置了其他執行緒池,原理與worker類似。

acceptor接收socket後,不是直接使用worker中的執行緒處理請求,而是先將請求傳送給了poller,而poller是實現nio的關鍵。acceptor向poller傳送請求通過佇列實現,使用了典型的生產者-消費者模式。在poller中,維護了乙個selector物件;當poller從佇列中取出socket後,註冊到該selector中;然後通過遍歷selector,找出其中可讀的socket,並使用worker中的執行緒處理相應請求。與bio類似,worker也可以被自定義的執行緒池代替。

通過上述過程可以看出,在nioendpoint處理請求的過程中,無論是acceptor接收socket,還是執行緒處理請求,使用的仍然是阻塞方式;但在「讀取socket並交給worker中的執行緒」的這個過程中,使用非阻塞的nio實現,這是nio模式與bio模式的最主要區別(其他區別對效能影響較小,暫時略去不提)。而這個區別,在併發量較大的情形下可以帶來tomcat效率的顯著提公升:

目前大多數http請求使用的是長連線(http/1.1預設keep-alive為true),而長連線意味著,乙個tcp的socket在當前請求結束後,如果沒有新的請求到來,socket不會立馬釋放,而是等timeout後再釋放。如果使用bio,「讀取socket並交給worker中的執行緒」這個過程是阻塞的,也就意味著在socket等待下乙個請求或等待釋放的過程中,處理這個socket的工作執行緒會一直被占用,無法釋放;因此tomcat可以同時處理的socket數目不能超過最大執行緒數,效能受到了極大限制。而使用nio,「讀取socket並交給worker中的執行緒」這個過程是非阻塞的,當socket在等待下乙個請求或等待釋放時,並不會占用工作執行緒,因此tomcat可以同時處理的socket數目遠大於最大執行緒數,併發效能大大提高。

再回顧一下tomcat處理請求的過程:在accept佇列中接收連線(當客戶端向伺服器傳送請求時,如果客戶端與os完成三次握手建立了連線,則os將該連線放入accept佇列);在連線中獲取請求的資料,生成request;呼叫servlet容器處理請求;返回response。

相對應的,connector中的幾個引數功能如下:

accept佇列的長度;當accept佇列中連線的個數達到acceptcount時,佇列滿,進來的請求一律被拒絕。預設值是100。

tomcat在任意時刻接收和處理的最大連線數。當tomcat接收的連線數達到maxconnections時,acceptor執行緒不會讀取accept佇列中的連線;這時accept佇列中的執行緒會一直阻塞著,直到tomcat接收的連線數小於maxconnections。如果設定為-1,則連線數不受限制。

預設值與聯結器使用的協議有關:nio的預設值是10000,apr/native的預設值是8192,而bio的預設值為maxthreads(如果配置了executor,則預設值是executor的maxthreads)。

在windows下,apr/native的maxconnections值會自動調整為設定值以下最大的1024的整數倍;如設定為2000,則最大值實際是1024。

請求處理執行緒的最大數量。預設值是200(tomcat7和8都是的)。如果該connector繫結了executor,這個值會被忽略,因為該connector將使用繫結的executor,而不是內建的執行緒池來執行任務。

maxthreads規定的是最大的執行緒數目,並不是實際running的cpu數量;實際上,maxthreads的大小比cpu核心數量要大得多。這是因為,處理請求的線**正用於計算的時間可能很少,大多數時間可能在阻塞,如等待資料庫返回資料、等待硬碟讀寫資料等。因此,在某一時刻,只有少數的線**正的在使用物理cpu,大多數執行緒都在等待;因此執行緒數遠大於物理核心數才是合理的。

換句話說,tomcat通過使用比cpu核心數量多得多的執行緒數,可以使cpu忙碌起來,大大提高cpu的利用率。

(1)maxthreads的設定既與應用的特點有關,也與伺服器的cpu核心數量有關。通過前面介紹可以知道,maxthreads數量應該遠大於cpu核心數量;而且cpu核心數越大,maxthreads應該越大;應用中cpu越不密集(io越密集),maxthreads應該越大,以便能夠充分利用cpu。當然,maxthreads的值並不是越大越好,如果maxthreads過大,那麼cpu會花費大量的時間用於執行緒的切換,整體效率會降低。

(2)maxconnections的設定與tomcat的執行模式有關。如果tomcat使用的是bio,那麼maxconnections的值應該與maxthreads一致;如果tomcat使用的是nio,那麼類似於tomcat的預設值,maxconnections值應該遠大於maxthreads。

(3)通過前面的介紹可以知道,雖然tomcat同時可以處理的連線數目是maxconnections,但伺服器中可以同時接收的連線數為maxconnections+acceptcount 。acceptcount的設定,與應用在連線過高情況下希望做出什麼反應有關係。如果設定過大,後面進入的請求等待時間會很長;如果設定過小,後面進入的請求立馬返回connection refused。

詳解tomcat的連線數與執行緒池

1 connector的protocol t11 2 如何選擇protocol t12 3 bio nio有何不同 t13 1 acceptcount t21 2 maxconnections t22 3 maxthreads t23 4 引數設定 t24 1 連線數 t41 2 執行緒 t42 一...

詳解tomcat的連線數與執行緒池

詳解tomcat的連線數與執行緒池 1 connector的protocol connector在處理http請求時,會使用不同的protocol。不同的tomcat版本支援的protocol不同,其中最典型的protocol包括bio nio和apr tomcat7中支援這3種,tomcat8增加...

tomcat 併發連線數 增大執行緒池

最近伺服器容易死掉,就尋找增大併發執行緒池的設定。maxthreads 最大執行緒數預設200 minprocessors 最小空閒連線線程數,用於提高系統處理效能,預設值為10 maxprocessors 最大連線線程數,即 併發處理的最大請求數,預設值為75 acceptcount 允許的最大連...