天天看點

并發和Tomcat線程數

轉載 http://www.360doc.com/content/19/0711/13/65266117_848050686.shtml

最近一直在解決線上一個問題,表現是:

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狀态:

并發和Tomcat線程數

于是大夥都開始糾結為什麼線程會漲到3000,而且發現即使峰值過了線程數并不會降下來。

我們首先想到的是:後端應用的處理瞬間比較慢,“堵住了”導緻前端線程數漲了起來。但是優化一個版本上線後發現雖然漲的情況有所好轉,但是最終線程池還是會達到3000這個最大值。

==================================分割線=========================================

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

1、首先是為什麼線程不釋放的問題?

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

Tomcat啟動時如果沒有請求過來,那麼線程數(都是指線程池的)為0;

一旦有請求,Tomcat會初始化minSapreThreads設定的線程數;

Tomcat不會主動對線程池進行收縮,除非确定沒有任何請求的時候,Tomcat才會将線程池收縮到minSpareThreads設定的大小;

Tomcat6之前的版本有一個maxSpareThreads參數,但是在7中已經移除了,是以隻要前面哪怕隻有一個請求,Tomcat也不會釋放多于空閑的線程。

至于Tomcat為什麼移除maxSpareThreads這個參數,我想也是出于性能的考慮,不停的收縮線程池性能肯定不高,而多餘的線程處于等待狀态的好處是一有新請求過來立刻可以處理。

而且大量的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這兩個參數:maxThreads是指Tomcat線程池做多能起的線程數,而maxConnections則是Tomcat一瞬間做多能夠處理的并發連接配接數。比如maxThreads=1000,maxConnections=800,假設某一瞬間的并發時1000,那麼最終Tomcat的線程數将會是800,即同時處理800個請求,剩餘200進入隊列“排隊”,如果acceptCount=100,那麼有100個請求會被拒掉。

注意:根據前面所說,隻是并發那一瞬間Tomcat會起800個線程處理請求,但是穩定後,某一瞬間可能隻有很少的線程處于RUNNABLE狀态,大部分線程是TIMED_WAITING,如果你的應用處理時間夠快的話。是以真正決定Tomcat最大可能達到的線程數是maxConnections這個參數和并發數,當并發數超過這個參數則請求會排隊,這時響應的快慢就看你的程式性能了。

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