只針對BIO模式,目標請求會sleep兩秒再返回結果,通過jmeter測試工具進行並發測試
操作系統:windows && linux
tomcat7測試:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" maxThreads="1" acceptCount="2"
redirectPort="8443" />
文檔:http://localhost:8080/docs/config/http.html
官方解釋:acceptCount對暫時無法執行的請求進行隊列保存,超出設置則拒絕連接。
測試發現無法限制住最大並發數,所有請求都可以依次執行,每次有1個線程執行(maxThreads=1)
最大並發數如果過大,大於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 有詳細的源碼分析