1. 程式人生 > >tomcat7關鍵配置和執行緒

tomcat7關鍵配置和執行緒

1. tomcat 7的一些關鍵引數配置

配置檔案conf/server.xml

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

沒有指定Executor,則使用internal executor,使用的是jdk的執行緒池。本文討論internal executor。

maxThreads – 預設200,最大執行緒數,為執行緒池最大大小(ThreadPoolExecutor的maximumPoolSize)

maxConnections – 最大連線數,預設為maxThreads

acceptCount – 預設100,監聽socket的 backlog。

minSpareThreads – 預設min(10,maxThreads) ,等同於ThreadPoolExecutor的corePoolSize

maxKeepAliveRequests – 預設100,一條長連線服務的http請求最大數目(超過了關閉之),-1表示不受數目影響。

connectionTimeout – 預設60000ms,accept一個連線後,等URI的最長時間。

keepAliveTimeout – 預設為connectionTimeout,一個連線等待下一個http請求到來的最長時間(過期關閉該連線)

2. 主要執行緒

2.1 http監聽執行緒

程式碼為:JIoEndpoint$Acceptor.run\
執行緒名為:http-bio-監聽埠-Acceptor-0

關鍵邏輯

public void run() {
    while (running) {
        // 連線數到了maxConnections則等待,否則計數器加1
        countUpOrAwaitConnection();
        Socket socket = accept();
        封裝socket為runnable物件(JIoEndpoint$SocketProcessor物件)提交給執行緒池;
    }
}

注: 執行緒池使用的佇列為TaskQueue extends LinkedBlockingQueue,其過載了offer方法。所以執行緒池的行為與JDK裡的稍有不同:連線數小於minSpareThreads,來一個連線建個執行緒;minSpareThreads<=連線數<maxThreads, 有空閒執行緒則新連線用空閒執行緒,無則建個執行緒; 連線數>=maxThreads,則新連線進入佇列。

分析: accept了maxConnections個連線後,1. 如果有新連線過來,則進入監聽套接字的backlog; 2.如果maxConnections>maxThreads,那麼maxConnections-maxThreads個連線進入執行緒池的佇列裡。

分析: nginx反向代理到tomcat時,出現過這麼個情況:nginx部分日誌顯示上游響應時間很長,但是tomcat access log顯示處理時間都很短,這是為什麼呢? 一個可能的原因是socket連線數滿了(並且每個長連線服務100個http後關閉,才處理新連線),新連線進入backlog,nginx的時間表現為backlog呆的時間+執行緒池佇列呆的時間+處理時間即accesslog裡的時間,而tomcat access日誌裡的時間為請求接收直到響應傳送完畢的時間。

conf/server.xml檔案裡,

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b %D" />

%D為毫秒,即為access log裡的時間

2.2 工作執行緒(執行緒池)

執行緒池在在AbstractEndpoint中建立。

AbstractEndpoint<Socket>
        |
    JIoEndpoint

工作執行緒程式碼:JIoEndpoint$SocketProcessor.run() \
執行緒名:http-bio-埠號-exec-

關鍵邏輯

public void run() {
    try {
        處理socket(一次http請求)
    } finally {
        不關閉socket(長連線),則封裝socket為runnable物件(JIoEndpoint$SocketProcessor物件)提交給執行緒池;
    }
}

從單個執行緒來看:服務一個http請求後,將socket放到執行緒池,自己再去任務佇列裡取任務。 這種方式的好處是 連線數多於執行緒數時,所有連線都公平的輪詢得到處理;缺點是socket任務不斷的入隊出隊在頻率極高下也會是個瓶勁。

2.3 命令監聽執行緒

conf/server.xml檔案裡

<Server port="8005" shutdown="SHUTDOWN">
</Server>

接收shutdown 之類的命令