tcp隊列和tomcat接受請求相關參數圖
該圖特別重要!!!
該圖特別重要!!!
該圖特別重要!!!
它是壓測實驗的靈魂,流程中經過的參數,將會是性能調優的找瓶頸的關鍵,那些參數大那些參數小會產生什麼問題等就分析這個流程就完了。
PS:網上將tomcat好多比較久遠都是用修改tomcat的配置文件,這裏我們使用SpringBoot方式的tomcat,所以參數設置也是SpringBoot的參數設置方式。
tomcat服務器啓動:【後邊有源碼可以看到】
1)TOMCAT創建ServerSocket綁定端口同時設置backlog即tcp的全連接隊列大小,告知操作系統tcp半連接隊列大小,操作系統默認值爲128和tocmat設置的值取最小值!!!因此當tomcat要設置大於128的時候需要修改操作系統的tcp全連接隊列的默認值
2)Tomcat 創建處理請求的線程池、Acceptor線程、Poller線程
tomcat服務器接收請求:外部客戶端(瀏覽器、app、Jmeter)發送請求到服務端過程
1)Socket發送第一握手會被放入到tcp半連接隊列【SYN_SENT】
2)Socket發送tcptcp三次握手成功操作系統將其從半連接隊列轉移到全連接隊列【ESTABLISHED】
3) Acceptor線程的run方法在執行:
3.1) tcp全連接隊列進入到tomcat,tomcat增加一個連接數(AQS方式增加)
3.2) serverSocket#accept之後放入到event隊列
4)Poller線程的run方法在執行:
從3)步驟Acceptor線程放入的event的隊列消費event,提交給線程池,交給Servlet處理
重要參數含義
連接數:指的是TCP連接數
TCP連接數=操作系統的TCP全連接數+Tomcat的maxConnection
後續會在壓測我們用一些工具組合看到ESTABLISHED、SYN_SENT、connectionCount以及currentThreadsBusy的證明
jmeter的http請求的connect超時時間:跟服務器TCP三次握手成功後進入全連接隊列需要的時間
可以通過增大jmeter的請求線程數量,減少tomcat的連接數和操作系統的全連接隊列查看半連接狀態和全連接狀態,過了超時時間之後全連接狀態全無,同時jmeter報錯
acceptCount: ServerSocket設置傳遞給操作系統的tcp的全連接隊列大小
tomcat啓動階段創建ServerSocketChannel時設置tomcat源碼的如下:
ServerSocketChannel#bind(addr,backlog) backlog就是acceptCount。
上面的說明的流程中隱含着一些設定會導致的一些後果:
實驗步驟
壓測實驗準備
實驗工具
- 會使用jmeter壓測,設置連接超時時間、響應超時時間
- tcp連接住狀態查看 netstat -nat | grep 端口號
- 使用JMX的MBean,tomcat參數可以通過jconsole等工具的MBean查看
- 需要懂tomcat源碼,java的AQS、tcp、nio
TCP知識補充
TCP狀態機
TCP三次握手四次揮手
TCP三次握手時候的兩個隊列
Client: 發送 SYN,連接狀態進入 SYN_SENT
Server: 收到 SYN, 創建連接狀態爲 SYN_RCVD/SYN_RECV 的 Socket,響應 SYN/ACK
Client: 收到 SYN/ACK,連接狀態從 SYN_SENT 變爲 ESTABLISHED,響應 ACK
Server: 收到 ACK,連接狀態變爲 ESTABLISHED
此時,雙方的 Socket 都已經進入了 ESTABLISHED 狀態,接下來就可以開始交換數據了。
從上面的過程中我們可以看出,Server 需要兩個隊列,分別存儲 SYN_RCVD 狀態的連接和 ESTABLISHED 狀態的連接,這就是半連接隊列和全連接隊列。
實驗參數設置
-
TCP全連接隊列mac和linux下全連接隊列默認都是 128
- 查看:sudo sysctl -a|grep somaxconn
- mac設置:sudo sysctl -w kern.ipc.somaxconn=2
- linux設置:sudo sysctl -w net.core.somaxconn=2
-
創建一個SpringBoot應用 workbench
- 構造一個請求耗時方法
@Slf4j @RestController @RequestMapping("/test") public class TestController { /** * @description 方法耗時工具方法,如下功能: * 1)制定任務耗時 * 2)耗時比較大,可以hold住線程 讓tomcat線程打滿 * @author yzMa * @date 2019/11/5 * @param * @return */ @GetMapping("/task/{cost}") public String taskCost(@PathVariable long cost) throws InterruptedException { Thread.sleep(cost); return "taskCost-"+cost; } }
-
tomcat設置
- server.tomcat.max-threads: 4
- server.tomcat.max-connections: 5
- server.tomcat.accept-count: 3
-
tomcat啓動腳本
- nohup java -jar -Dcom.sun.management.jmxremote=true -Djava.rmi.server.hostname=localhost -Dcom.sun.management.jmxremote.port=7878 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false workbench-0.0.1-SNAPSHOT.jar &
-
jmeter設置
- 15個線程1秒發送完
- http請求設置
- /test/task/60000表示在服務端sleep1分鐘(可以看下上面方法的邏輯)目的爲了執行時間長一些hold住線程,方便我們用工具查看各個參數的運行時的值。
小工具
nc -4 localhost 7777
ss -tln
Recv-Q 表示全連接隊列當前的長度,Send-Q 表示全連接隊列配置的長度
壓測實驗證明
再統計說明下實驗參數:
jmeter設置連接超時【tcp的全連接】時間爲10秒鐘
mac設置的tcp全連接數爲2
tomcat設置的acceptCount爲3,所以取最小值,所以就是tcp的全連接隊列就是2
tomcat設置的全連接數爲5
tomcat的線程數爲4個
Controller耗時60000ms即1分鐘
點擊壓測按鈕,jmeter提交請求。
jconsole查看
4個tomcat線程全部都在忙(Worker的屬性exclusiveThread!=null)
所有的tomcat線程數4個全被hold住60000ms即1分鐘,
tomcat的mbean應該看到所有的連接數爲5
如上有5個tomcat的maxConnection全都被使用,4個線程全在忙。
此時在看操作系統的TCP連接狀態,15個jmeter線程,5個成功建立TCP全連>接並進入到佔用了tomcat的連接,再加上TCP全連接隊列是2,總共有【5+2=7】7個全>連接(ESTABLISED)所以剩下的【15-7=8】8個半連接不會進入到全連接隊列,而是在半連接隊列。
- netstat -nat |grep 端口號 查看鏈接狀態
完全符合上面的結論。
這裏解釋下爲什麼要ESTABLISED個數要除以2,這是因爲我本地,部署着服務Server,同時又是我本地訪問client。
如下是截圖是證明:
- 我自己訪問我自己一次 會看到兩個ESTABLISED
- 其它人訪問我一次只會看到一個ESTABLISED
那麼在此經過jmeter設置的連接超時時間10000ms即10s之後再看
jconsole看到的連接數和線程正在忙的數量跟上面一樣不在貼圖了。
原來的8個半連接超時了,全都沒有了,同時jmeter報錯了
錯誤信息:
org.apache.http.conn.ConnectTimeoutException: Connect to localhost:7777 timed out
5個tomcat連接maxConnection,被4個tomcat線程執行【其中一個一定是在>等待,再加上還有連個tcp隊列裏邊的連接還沒有進入tomcat,所以此時總共有三個連接在等待
,可以得出結論,tomcat連接數和tcp全連接隊列越大,線程數越少的情況下, >會有請求一直等待,造成用戶得不到響應,直到成客戶端超時】
再往過50s之後即1分鐘後
其它請求一次類推,一次最多進入處理4個,有一個等待或者沒有等待【取決於在當前的連接數是否】。
小結:
tomcat核心參數:
server:
tomcat:
max-threads: 4
max-connections: 5
accept-count: 3
TCP的半連接隊列【未完成三次握手進入的隊列】
TCP的全連接隊列【三次握手完成從半連接隊列轉移到的隊列】
查看操作系統的全連接隊列: sudo sysctl -a |grep somaxconn 默認都是128
mac設置:sudo sysctl -w kern.ipc.somaxconn=2
linxu設置:sudo sysctl -w net.core.somaxconn=2
tomcat的acceptCount 是ServerScoket#bind(arrd,backlog) 是告知操作系統tcp全連接的隊列長度,操作系統默認爲128,默認值和tomcat傳遞值取小值。所以當tomcat設置大於128的時候需要設置操作系統的值。
maxConnection連接數+tcp全連接隊列(三次握手全結束)=tcp全連接的總數
這個值如果大於tomcat核心線程數就在高併發下(所有線程全都在執行任務)會出現等待,線程處理越慢,剩下多餘的tcp連接等待時間就越長。
jmeter的連接超時時間就是 建立tcp全連接的時候等待時間,如果tomcat的maxConnection的指標較小或者jmeter的請求線程比較多,
即:jmeter的線程數-tcp全連接隊列-tomcat最大連接數>0,大於0的jmeter線程就會出現半連接
如果maxConnection+min(acceptCount,tcp全連接隊列大小) - tomcat線程數>0 會有TCP全連接等待問題,
如果tomcat處理比較慢,tcp半連接的線程會超時 從半連接隊列移除 jmeter失敗
以往看書,帖子都是很理性,這樣有實驗數據就變得很感性。
看得見摸得着的東西,記憶纔會深刻。