tomcat參數以及tcp連接-性能調優實驗

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失敗

以往看書,帖子都是很理性,這樣有實驗數據就變得很感性。
看得見摸得着的東西,記憶纔會深刻。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章