聊下併發和Tomcat線程數

最近一直在解決線上一個問題,表現是:Tomcat每到凌晨會有一個高峯,峯值的併發達到了3000以上,最後的結果是Tomcat線程池滿了,日誌看不少請求超過了1s。服務器性能很好,Tomcat版本是7.0.54,配置以下:前端

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" 
  maxThreads="3000" minSpareThreads="800"/>
<Connector executor="tomcatThreadPool" port="8084" protocol="org.apache.coyote.http11.Http11AprProtocol"
   connectionTimeout="60000" keepAliveTimeout="30000"
   maxKeepAliveRequests="8000" maxHttpHeaderSize="8192"
   URIEncoding="UTF-8" enableLookups="false"   acceptCount="1000" disableUploadTimeout="true"
    redirectPort="8443" />

過後thread dump看其實真正處於RUNNABLE狀態的線程不多,絕大部分線程都處於TIMED_WAITING狀態:apache

輸入圖片說明

因而大夥都開始糾結爲何線程會漲到3000,並且發現即便峯值過了線程數並不會降下來。後端

咱們首先想到的是:後端應用的處理瞬間比較慢,「堵住了」致使前端線程數漲了起來。可是優化一個版本上線後發現雖然漲的狀況有所好轉,可是最終線程池仍是會達到3000這個最大值。tomcat

==================================分割線=========================================服務器

以上是大背景,中間的過程省略,直接跟各位說下目前我獲得的結論:多線程

1 - 首先是爲何線程不釋放的問題

簡單說下我驗證的Tomcat(7.0.54)線程池大概的工做機制併發

  • Tomcat啓動時若是沒有請求過來,那麼線程數(都是指線程池的)爲0
  • 一旦有請求,Tomcat會初始化minSapreThreads設置的線程數
  • Tomcat不會主動對線程池進行收縮,除非肯定沒有任何請求的時候,Tomcat纔會將線程池收縮到minSpareThreads設置的大小
  • Tomcat6以前的版本有一個maxSpareThreads參數,可是在7中已經移除了,因此只要前面哪怕只有一個請求,Tomcat也不會釋放多於空閒的線程

至於Tomcat爲何移除maxSpareThreads這個參數,我想也是出於性能的考慮,不停的收縮線程池性能確定不高,而多餘的線程處於等待狀態的好處是一有新請求過來馬上能夠處理。less

並且大量的Tomcat線程處於等待狀態不會消耗CPU,可是會消耗一些JVM存儲。性能

2 - 爲何線程池會滿

這是我如今糾結的核心。究竟是不是應用的性能慢致使的,我如今的結論是有關係,但關鍵是併發。 Tomcat的線程池的線程數跟你的瞬間併發有關係,好比maxThreads設置爲1000,當瞬間併發達到1000那麼Tomcat就會起1000個線程來處理,這時候跟你應用的快慢關係不大。 那麼是否是併發多少Tomcat就會起多少個線程呢?這裏還跟Tomcat的這幾個參數設置有關係,看官方的解釋是最靠譜的:優化

maxThreads:The maximum number of request processing threads to be created by this Connector, 
which therefore determines the maximum number of simultaneous requests that can be handled. 
If not specified, this attribute is set to 200. If an executor is associated with this connector, 
this attribute is ignored as the connector will execute tasks using the executor rather than an internal thread pool.
maxConnections:The maximum number of connections that the server will accept and 
process at any given time. When this number has been reached, the server will 
accept, but not process, one further connection. This additional connection be 
blocked until the number of connections being processed falls below maxConnections 
at which point the server will start accepting and processing new connections 
again. Note that once the limit has been reached, the operating system may still 
accept connections based on the acceptCount setting. The default value varies by 
connector type. For BIO the default is the value of maxThreads unless an Executor 
is used in which case the default will be the value of maxThreads from the 
executor. For NIO the default is 10000. For APR/native, the default is 8192.……
acceptCount:The maximum queue length for incoming connection requests when all
 possible request processing threads are in use. Any requests received when the 
queue is full will be refused. The default value is 100.
minSpareThreads:The minimum number of threads always kept running. If not specified, the default of 10 is used.

我簡單理解就是:

  • maxThreads - Tomcat線程池最多能起的線程數
  • maxConnections - Tomcat最多能併發處理的請求(鏈接)
  • acceptCount - Tomcat維護最大的對列數
  • minSpareThreads - Tomcat初始化的線程池大小或者說Tomcat線程池最少會有這麼多線程。

比較容易弄混的是maxThreads和maxConnections這兩個參數:

  1. maxThreads是指Tomcat線程池作多能起的線程數

  2. maxConnections則是Tomcat一瞬間作多可以處理的併發鏈接數。好比maxThreads=1000,maxConnections=800,假設某一瞬間的併發時1000,那麼最終Tomcat的線程數將會是800,即同時處理800個請求,剩餘200進入隊列「排隊」,若是acceptCount=100,那麼有100個請求會被拒掉。

    注意:根據前面所說,只是併發那一瞬間Tomcat會起800個線程處理請求,可是穩定後,某一瞬間可能只有不多的線程處於RUNNABLE狀態,大部分線程是TIMED_WAITING,若是你的應用處理時間夠快的話。因此真正決定Tomcat最大可能達到的線程數是maxConnections這個參數和併發數,當併發數超過這個參數則請求會排隊,這時響應的快慢就看你的程序性能了。

以上的結論都是我我的驗證和總結,若有不對,跪求指正!!!

相關文章
相關標籤/搜索