首頁>技術>

前言:

今天測試部門的小夢找到我,委屈巴巴的說我寫的介面有問題,因為她對這個介面進行壓力測試時,發現系統的吞吐量一直上不去,並且 應用伺服器 (部署介面專案的伺服器) 的CPU、記憶體等資源的使用率也一直很低,導致一直無法測試出這個介面的壓力峰值。

聽小夢說完後,自己心想介面都測試了好幾遍了, 介面程式碼 絕對不可能有問題的,再說了,有問題也不能承認呀,看來得往別的地方上扯扯呀;然後我說道,介面應該是沒問題的,可能是專案環境部署時有些引數沒進行調優吧,例如:連線數大小設定、JVM引數設定、資料庫引數最佳化等;

然後我接著說道,專案是誰給你部署配置的呀,小夢說是小王部署的,然後今天小王也沒來上班;我說道,小王沒來上班呀,沒事,讓我來,我這bug絕緣體質,任何問題遇到我都會退避三舍的。

上面的情景,我相信大家都可能會遇到過的;接下來我們就透過這次排查壓測問題來聊聊一個 單體系統 的效能最佳化應該考慮哪些點,以及對這些點該怎麼進行調優 。

本文主線如下圖:專案部署環境:

在進行這次壓測問題排查前,先透過下圖瞭解下 介面專案的情況、專案部署的環境、JMeter測試指令碼 的配置情況;

上圖中的介面專案、Redis、MySql 都是單機安裝部署的 。

注意:在進行下文之前,我們還需要記住一個前提:本專案中的介面程式碼是不存在問題的,並且資料庫查詢SQL都是最優的,都是走索引查詢的,本次壓力測試問題不是由於程式碼導致的,而是由於各種引數未調優造成的;程式碼調優和SQL調優在編碼階段就已經完成了。

簡單效能調優的點:

在上文中的專案部署情況和測試指令碼的配置情況下進行壓力測試時,就出現了小夢說的系統吞吐量上不去,以及應用伺服器(部署介面專案的伺服器)的 CPU 等資源使用率都很低的情況;

檢視 JMeter 測試時系統的吞吐量如下圖:檢視應用伺服器(部署介面專案的伺服器)的CPU使用率很低:

按照測試指令碼的50個併發以及介面中含有計算密集型操作(加解密和驗籤)的情況,CPU是不應該這麼低的;哎!什麼鬼嘛?

別急,下面我們就展開具體的排查過程,並在排查過程中逐步瞭解各個調優的點;

排查過程就是按照文章開頭中的 本文主線 圖片中 簡單效能調優的點 展開的 。

排查Tomcat聯結器引數配置:Tomcat 聯結器引數配置只需要考慮以下幾個方面:Connector使用哪種 protocol 協議HTTP/1.1:預設值,使用的協議與Tomcat版本有關建議使用 NIO2 :org.apache.coyote.http11.Http11Nio2Protocol除了NIO2協議外,還有 BIO、NIO、APR 協議,可以自行去查閱資料;acceptCount:等待佇列的長度;當等待佇列中連線的個數達到acceptCount時,說明佇列已滿,再進來的請求一律被拒絕;預設值是100。maxConnections:Tomcat 在任意時刻接收和處理的最大連線數;當連線數達到最大值maxConnections後,Tomcat會繼續接收連線,直到accept等待佇列填滿。如果最大連線數設定為-1,則表示禁用maxconnections功能,表示不限制tomcat容器的連線數;最大連線數預設值與聯結器使用的 protocol 協議有關:NIO的預設值是10000;APR/native的預設值是8192;BIO的預設值為maxThreads(如果配置了Executor,則預設值是Executor的maxThreads);maxThreads:每一次HTTP請求到達 Tomcat,都會建立一個執行緒來處理該請求,那麼最大執行緒數決定了Tomcat 可以同時處理多少個請求。maxThreads 預設200;建議:maxThreads應該設定大些,以便能夠充分利用CPU;當然,也不是越大越好,如果maxThreads過大,那麼CPU會花費大量的時間用於執行緒的上下文切換,整體效率可能會降低;這需要根據自己專案的情況和伺服器硬體配置情況配置合適的值即可;下面就是本次壓測時配置的Tomcat聯結器引數:
<Connector         port="8080"          protocol="org.apache.coyote.http11.Http11Nio2Protocol"         maxThreads="1000"          maxConnections="2000"         acceptCount="1000"         connectionTimeout="20000"          redirectPort="8443"          enableLookups="false"          maxPostSize="10485760"          compression="on"          disableUploadTimeout="true"          compressionMinSize="2048"          acceptorThreadCount="2"          compressableMimeType="text/html,text/xml,text/css,text/javascript"          URIEncoding="utf-8" />

上面聯結器中配置引數:的

聯結器使用的 protocol 協議是 NIO2最大執行緒數 maxThreads="1000"最大連線數 maxConnections="2000"等待佇列大小 acceptCount="1000"

根據上面聯結器配置引數,和壓測指令碼配置的併發請求才50個,確定壓測問題應該不是由於聯結器引數造成的。

其實在檢視Tomcat配置檔案 server.xml 中的聯結器引數前,可以使用下面這個命令先直接統計出 JMeter與Tomcat建立的Http連線數: 注意 8080 指的是 Tomcat 埠號,使用前改成你的Tomcat配置的埠

netstat -pan | grep 8080 | wc -l

如果統計出的連線數與測試指令碼中配置的併發數的話差不多一致的話,說明當前聯結器是可以滿足連線請求處理的,初步可以判斷不是由於聯結器引數造成的本次壓測問題;然後再可以去檢視配置檔案中的引數進行確認下。

排查 JVM 引數配置:

注意:JVM 引數配置比較複雜,這塊的引數調優需要根據具體專案情況、伺服器硬體配置等進行合理配置。

檢視當前壓測的介面專案 JVM 配置引數:直接使用命令檢視:ps -ef | grep tomcat結果如下:也可以直接去 catalina.sh 配置檔案中檢視:JAVA_OPTS=" -server -Xms6000m -Xmx6000m "也可以直接透過 jmap 命令檢視 JVM 的堆的配置資訊:注意:下面命令中的 129761 是tomcat的 pid 程序號;jmap -heap 129761結果如下:

看到這,應該會有人問,為什麼你的初始堆大小和最大堆大小(-Xms6000m -Xmx6000m)設定為一樣呢?

因為如果虛擬機器啟動時設定的初始堆記憶體比較小,這個時候又需要初始化很多物件資料,那麼虛擬機器就必須不斷地擴大堆記憶體,這樣也會產生一些消耗的。

排查下是否由於JVM引數配置的不合理導致:使用上面提到的 jmap 命令檢視下當前堆記憶體的使用情況jmap -heap 129761

透過檢視上面的堆記憶體的使用情況,發現還有很多空間使用呢,應該不是由於JVM引數不合理導致的壓測問題;

但是咱彆著急,咱再檢視下JVM中垃圾回收GC的情況;

使用命令 jstat 檢視垃圾回收GC的情況:注意:129761 為Tocamt的pid; 1000 表示間隔時間毫秒,10 表示輸出10次GC情況jstat -gc 129761 1000 10

看了上面的GC情況,發現垃圾回收也沒什麼異常,FGC總共就3次,透過檢視的堆使用情況和GC垃圾回收的情況可以確認壓測的問題不是由於JVM的引數導致的。

看來這次壓測的問題挺棘手呀,到現在還沒有確認出問題原因呢!難道我這bug絕緣體質由於 今天吃的比較多 ,導致失效了嗎? 皺眉 ing . . . . .

看來還得接著排查,咱直接匯出當前介面服務應用程序的 執行緒堆疊資訊 看看吧。

使用命令jstack匯出執行緒棧資訊:注意:129761 為Tomcat 的pid, /usr/local/test.log 未生成的執行緒堆疊檔案 test.log 的存放地址jstack 129761 >> /usr/local/test.log匯出執行緒堆疊資訊後,使用命令搜尋出狀態為 WAITING 的執行緒;注意:找 State 為 WAITING 狀態的執行緒;命令如下:test.log 檔案為剛剛匯出的 執行緒堆疊資訊檔案cat test.log | grep -n20 " WAITING "在命令執行後的搜尋結果中發現了下圖內容:

除了執行緒狀態為 WAITING 的之外,還特別要注意 WAITING 狀態執行緒的方法呼叫棧中存在專案業務程式碼的;

上面圖片中就展示了執行緒狀態為 WAITING 等待的呼叫方法棧中有業務程式碼;業務程式碼是去Redis中查詢資料,上圖中第二個圈中的內容就是業務程式碼,並且這行程式碼上方是去Redis連線池中獲取連線,由此確定是由於Redis連接獲取不到,導致執行緒處於 WAITING 等待狀態;由於介面服務系統中很多執行緒獲取不到Redis連線導致阻塞,從而無法充分利用CPU;所以在壓測時,伺服器CPU的使用率也壓不上去,CPU的使用率很低;

至此,終於排查出了一處問題地方,不容易呀,感覺排查問題比寫程式碼困難多了!

上面透過執行緒的堆疊資訊排查出 Redis 連線不夠用,應該是 Redis 連線池中的最大連線數配置的比較小,那接下來就確定下連線池的配置資料。

排查連線池引數配置:

上文中,線上程的堆疊資訊中排查出存在很多執行緒由於獲取不到Redis連線導致處於等待阻塞狀態;

接下來,咱們就來具體統計下現在處於壓測中的應用伺服器(部署介面專案的伺服器)與Redis建立的連線數,順帶一起再排查下專案中使用的MySql 的連線池配置是否也存在問題?

使用命令檢視當前壓測中的應用伺服器與Redis建立的連線數注意:6379 為 redis的埠號netstat -pan | grep 6379 | wc -l執行命令查詢出與Redis建立的連線數為 10,這在測試指令碼50個併發請求下是一定不夠用的呀,應該是配置的Redis連線池的最大連線數比較小;然後透過看介面程式碼中的配置檔案發現,確實Redis連線池中的最大連線數配置是 10 個;最後修改了最大連線數為 300 個;注意:這個最大連線數需要根據專案情況和測試計劃來設定,不用設定的太大。接著,使用命令順帶統計下當前處於壓測中的應用伺服器(部署介面專案的伺服器)與MySql 建立的連線數注意:3306 為MySql 的埠號netstat -pan | grep 3306 | wc -l執行命令發現已經建立的MySql 連線數是 50,然後透過檢視配置檔案的連線池中最大的連線數為100,發現還沒有達到最大連線數,說明 JDBC 連線夠用,本次壓測問題不是由於JDBC連線不夠用導致的;並且在上文中檢視執行緒的堆疊資訊時,也沒有發現呼叫 JDBC 操作的執行緒處於 WAITING 等待狀態。

排查完了資料庫連線池了,咱們就再排查下MySql 資料庫是否進行了引數調優;

如果MySql 資料庫沒有進行調優的話,那在壓測時就會出現資料庫的讀寫效能比較差,也就是專案中在進行 JDBC 增刪改查操作時會比較慢,進而導致存在JDBC操作的執行緒執行慢,無法充分利用CPU的併發計算能力,所以也會導致應用伺服器的CPU使用率比較低,最終壓測時整個介面應用系統的吞吐量也很低 。

排查MySql 資料庫調優配置:

排查前先聊一個由於之前MySql 資料庫未進行最佳化,而導致的壓測問題。

MySql 資料庫未調優,引發的壓測問題 簡述:

之前壓測時的場景還原:

在壓測前所有該調優的地方都調了,就單獨忘記對MySql 資料庫進行調優了,最終導致對應用壓測時,發現應用伺服器(部署介面專案的伺服器)的CPU的使用率一直浮動在60%左右,始終壓不上去,即使加大壓測時的併發請求數,發現還是不行;

最終透過檢視MySql 伺服器的磁碟IO的情況和MySql 資料庫程序的CPU、記憶體等使用率發現MySql 沒有被最佳化,所以導致MySql 資料庫的讀寫能力被限制,進而導致應用併發處理能力下降,從而導致應用伺服器的CPU使用率一直升不上去。

使用命令統計下壓測時 MySql 資料庫所部署在的伺服器的磁碟IO效能:注意: 1 指的是 間隔時間1秒, 5 指的是統計5次 IO 的情況;iostat -xdk 1 5

結果如下:

只需要注意圖中圈起來的地方;await 指的是 磁碟讀寫時的等待時間,這個值越小越好;%util 指的是磁碟整體的負載情況,值越大說明磁碟負載快達到極限了。

使用命令檢視MySql 程序的CPU、記憶體的使用率:注意: 14208 是MySql 的pid;top -p 14208結果如圖:

注意:

當時就是透過這兩條命令統計出的資料,發現數據庫伺服器磁碟IO的負載一直處於90%,快達到極限了;但是MySql 程序的CPU、記憶體的使用率卻很低;

透過這兩條資料可以初步確定MySql 沒有進行最佳化;如果MySql 進行了調優,那麼MySql 的程序在壓測時的CPU、記憶體等使用率不可能這麼低,並且磁碟 IO 的負載也不會達到了極限;

得出初步結論後,然後去檢視MySql 資料庫的配置檔案 my.cnf, 發現確實沒有進行一些引數的最佳化,只是簡單配置了最大連線數為 500;注意,MySql 資料庫預設的最大連線數是 100 個;

最後在 my.cnf 配置檔案中簡單配置了下 innodb 儲存引擎緩衝池大小 等;這裡就不具體描述調優的引數了,因為調優引數都是需要根據專案情況、伺服器硬體配置等綜合考慮配置的;大家可以自行網上查閱MySql 資料庫調優的相關資料;

配置好後,重啟了MySql 資料庫,然後重新進行壓測,發現應用伺服器(部署介面專案的伺服器)的CPU使用率上去了,MySql 資料庫程序的CPU、記憶體等使用率也升上去了,並且磁碟IO的負載也降下來了,最後整個應用系統的吞吐量也上去了,也支援更大的併發數了。

說完了上面之前遇到的一個壓測小場景,咱們接著按照上面的排查步驟排查這次壓測的問題,發現MySql 資料庫已經進行了調優,磁碟IO的讀寫速度也是OK的,所以說本次壓測問題與MySql 資料庫引數配置無關。

排查完了MySql 資料庫後,咱接著排查下Linux伺服器的引數是否進行過調優吧!

排查Linux伺服器引數配置:

伺服器引數調優這塊目前瞭解的不多,只知道配置下伺服器的 檔案控制代碼數 fd ;建議將伺服器的檔案控制代碼數設定的大些;為什麼設定大些呢?

因為主流作業系統的設計是將 TCP/UDP 連線採用與檔案一樣的方式去管理, 即一個連線對應於一個檔案描述符(檔案控制代碼)

主流的 Linux 伺服器預設所支援最大 檔案控制代碼數 數量為 1024,當併發連線數很大時很容易因為 檔案控制代碼數 不足而出現 “open too many files” 錯誤,導致新的連線無法建立。

建議將 Linux 伺服器所支援的最大控制代碼數調高數倍(具體與伺服器的記憶體數量相關)。

使用命令檢視當前伺服器的檔案控制代碼數:ulimit -a結果如圖:

上圖中的檔案控制代碼數是已經修改過的;設定檔案控制代碼數可以設定臨時值,也可以設定永久值,建議設定成永久值,因為臨時值遇到一些情況會失效的。

配置檔案控制代碼數可以自行去網上查詢,資料很多的,本文就不做過多描述了。

聊完伺服器引數調優配置後,咱最後再聊聊 JMeter 測試工具在壓測時有沒有該注意的點!

JMeter 分散式測試:

這裡主要聊聊 JMeter 分散式測試的內容;什麼時候需要進行分散式測試,當你的單機 JMeter 伺服器由於硬體配置限制無法構建出足夠的併發壓力時,這時就需要進行分散式部署測試了;

怎麼判斷 單機的 JMeter 伺服器 已經無法構建出足夠的併發壓力了呢?

透過檢視 JMeter 測試伺服器的CPU的使用率情況,當 JMeter 測試伺服器的CPU使用率達到 90% 多時,此時就可以初步確認CPU無法再提供足夠的計算能力來構建併發壓力了;所以此時可以將單機的JMeter改為分散式JMeter進行測試,只有構建出足夠的壓力,才能將應用伺服器(部署介面專案的伺服器)的CPU、記憶體等資源的使用率壓上去,從而測試出介面的壓力峰值。

所以,當單機 JMeter 壓測時發現應用伺服器(部署介面專案的伺服器)CPU、記憶體等使用率壓不上去的話,也可以去排查下是否是由於JMeter伺服器硬體配置不足造成的;

本次壓力問題,發現 JMeter 測試伺服器在壓測時 CPU 的使用率達到了 80%多,說明此臺測試伺服器由於硬體限制也是無法構建更大的併發壓力了,所以建議將單機 JMeter 測試改為 分散式測試;

這裡 JMeter 分散式測試網上教程很多,本文就不做過多描述了。

到這裡,本人認為的所有該調優的點都排查完; 今天老百姓真呀真呀真高興!

本次排查總結:

排查完上面的幾個點後,最終發現了導致本次壓測問題的原因:

Redis 連線池的最大連線數配置的偏小JMeter 單機測試無法構造出足夠的併發壓力

正是由於上面的兩個問題導致了本次壓測出現了下面的問題:

壓測時應用系統的吞吐量偏低並且應用伺服器(部署介面專案的伺服器)的CPU、記憶體等使用率偏低;

在此,再說明下本次壓測專案就是一個簡單的單體專案,專案中沒有涉及到分庫分表、各種中介軟體以及分散式等,所以排查起來還相對簡單些 ,否則問題排查會更加困難的。

擴充套件

在排查問題時,如果有一些 排查工具 的話,將極大的方便我們排查問題,從而使我們快速的定位到問題原因!

下面就介紹兩個在排查問題非常好用的輔助工具:

Arthas : 阿里開源的線上Java應用診斷工具jvisualvm :JDK中自帶的 JVM 執行狀態監控工具

原文連結:https://segmentfault.com/a/1190000038467733

21
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • AMNESIA:33 多個TCP/IP開原始碼庫漏洞通告