只針對BIO模式,目標請求會sleep兩秒再返回結果,經過jmeter測試工具進行併發測試html
操做系統:windows && linuxlinux
tomcat7測試:windows
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" maxThreads="1" acceptCount="2"
redirectPort="8443" />tomcat
文檔:http://localhost:8080/docs/config/http.html併發
官方解釋:acceptCount對暫時沒法執行的請求進行隊列保存,超出設置則拒絕鏈接。socket
測試發現沒法限制住最大併發數,全部請求均可以依次執行,每次有1個線程執行(maxThreads=1)tcp
最大併發數若是過大,大於acceptCount值好幾倍,會隨機出現鏈接被拒絕。工具
進一步調查-----------------------------------------------------------------------------------------------------------------------------------源碼分析
首先關於tcp queue簡單介紹:測試
tcp三次握手:
第一次,客戶端發送syn,等待服務端確認,此時客戶端進入SYN_SEND狀態
第二次,服務端接收syn包並確認,發送syn+ack給客戶端,此時服務端進入SYN_RECV狀態
第三次,客戶端收到syn+ack,再次向服務端發送ack,此時客戶端進入ESTAB狀態(注意,此時服務端未必進入ESTAB狀態)
半鏈接隊列:服務端維護的與客戶端保持SYN_RECV狀態的鏈接隊列,等待客戶端回覆,當收到客戶端ack後,若是條件容許(全鏈接隊列未達到最大值),服務端進入ESTAB狀態,從半鏈接隊列移到全鏈接隊列的隊尾。
全鏈接隊列:完成三次握手等待accept。完成三次握手即進入了全鏈接隊列的隊尾,當進程調用accept時,全鏈接隊列中的隊頭項將返回給進程,並從隊列中移出鏈接。若是該隊列爲空,那麼進程將被投入睡眠,直到TCP在該隊列中放入一項才喚醒它。
在listen(int sockfd, int backlog)中,backlog在Linux 2.2以後表示的是已完成三次握手但還未被應用程序accept的隊列長度。
全鏈接隊列滿,半鏈接隊列未滿:
客戶端發出syn分節,服務端收下,並向客戶端發出syn+ack。
客戶端收到服務端syn+ack後,成爲ESTAB狀態,向服務端發送第三次握手ack。
服務端收到ack後,發現全鏈接隊列已滿,默認狀況下,服務端什麼也不作,狀態依然是SYN_RECV。
客戶端會重傳syn和ack,當達到必定的閾值(/proc/sys/net/ipv4/tcp_synack_retries)時,客戶端與服務端斷開鏈接,服務端刪除客戶端在半鏈接隊列中的syn分節。
全鏈接、半鏈接隊列都滿:
客戶端發出syn分節,服務端發現半鏈接隊列已滿,直接丟棄syn,使客戶端重傳syn。
客戶端重傳syn,再次到達服務端後,服務端發現已經重傳過,則收下,並告訴客戶端syn+ack。
後續流程與上述一致,相比之下,多了一次客戶端重傳syn分節。
修改tcp參數配置:
tcp_synack_retries和tcp_syn_retries定義SYN 的重試鏈接次數
/etc/rc.d/rc.local文件中追加:
sysctl -w net.ipv4.tcp_synack_retries=3
sysctl -w net.ipv4.tcp_syn_retries=3
重啓。
也可直接執行命令:
sysctl -w net.ipv4.tcp_synack_retries=1
sysctl -w net.ipv4.tcp_syn_retries=3
關於隊列的長度:
半鏈接隊列:≈2 * min(backlog, net.ipv4.tcpmax_syn_backlog)
全鏈接隊列:min(/proc/sys/net/core/somaxconn(本系統128), backlog),表示最多有 min() + 1個 ESTAB 的鏈接等待 accept()。
修改somaxconn,
在/etc/sysctl.conf中添加以下:
net.core.somaxconn = 2048
而後在終端中執行
sysctl -p
進一步試驗-------------------------------------------------------------------------------------------------------------------------------------------------------------------
在linux下,經過ss -ant進行觀察tcp queue
併發5個請求
acceptCount配置做爲socket中的backlog的值,若是acceptCount不設置或者設置爲0,則會取默認值,經測試是100。
全鏈接隊列大小爲 min(/proc/sys/net/core/somaxconn(本系統128), 2) + 1=3,即等待服務端accept的ESTAB狀態的鏈接最多有3個,如Recv-Q所示。
3個Recv-Q爲323(表示請求bytes數值)的ESTAB狀態的鏈接正等待server accept
1個Recv-Q爲0的ESTAB狀態的鏈接表示server端已經accept了請求,只是尚未返回結果(sleep中)。
1個SYN-RECV狀態是半鏈接狀態,位於半鏈接隊列當中(此時客戶端已經處於ESTAB狀態),全鏈接隊列已經滿了。
對於處在半鏈接狀態的鏈接,客戶端會定時重發,直至達到閾值,以下:
第一次握手
第二次握手
第三次握手
客戶端發送數據包
但因爲此時服務端未能將鏈接從半鏈接隊列移至全鏈接隊列,狀態依然是SYN-RECV,即未獲得服務端ack,所以客戶端繼續發數據包
發送幾回以後,服務端有了迴應,告訴客戶端說,你以前發的包丟了
而後客戶端也告訴服務端,丟失的狀態、對應的id號及次數,這裏的43就是第三次握手的43,失敗次數是1
而後客戶端又開始重傳了
終於服務端受不了了,此時客戶端服務端完全斷開。
經過調整ipv4.tcp_synack_retries和ipv4.tcp_syn_retries,能夠增長重試次數
結論:
對於tomcat中的acceptCount只是全鏈接隊列的大小,就是說客戶端和服務端都已是ESTAB狀態的鏈接,不考慮connectionTime的狀況,在此隊列中的鏈接最終都會被處理。對於大於acceptCount的鏈接請求,若是在tcp重試閾值範圍以內完成半鏈接到全鏈接的狀態轉換,那麼仍是有機會被服務端accept並處理的。
所以,不能說只要大於acceptCount的鏈接就必定被拒絕!
參考文獻:
http://www.cnblogs.com/leezhxing/p/5329786.html
http://blog.chinaunix.net/uid-24782829-id-3456109.html
http://www.cnblogs.com/menghuanbiao/p/5212131.html
http://www.cnblogs.com/zengkefu/p/5606696.html 有詳細的源碼分析