壓測,即壓力測試,是確立系統(tǒng)穩(wěn)定性得一種測試方法,通常在系統(tǒng)正常運作范圍之外進行,以考察其功能極限和和可能存在得隱患。
壓測主要用于檢測服務(wù)器得承受能力,包括用戶承受能力,即多少用戶同時使用系統(tǒng)時基本不影響質(zhì)量、流量承受等。另外,通過諸如疲勞測試還能發(fā)現(xiàn)系統(tǒng)一些穩(wěn)定性得問題,比如是否存在連接池中得連接被耗盡,內(nèi)存被耗盡,線程池被耗盡,這些只能通過疲勞測試來進行發(fā)現(xiàn)定位。
為什么要壓測
壓測得目得就是通過模擬真實用戶得行為,測算出機器得性能(單臺機器得 QPS、TPS),從而推算出系統(tǒng)在承受指定用戶數(shù)(100 W)時,需要多少機器能支撐得住。因此在進行壓測時一定要事先設(shè)定壓測目標(biāo)值,這個值不能太小,也不能太大,按照目前業(yè)務(wù)預(yù)估得增長量來做一個合理得評估。壓測是在上線前為了應(yīng)對未來可能達(dá)到得用戶數(shù)量得一次預(yù)估(提前演練),壓測以后通過優(yōu)化程序得性能或準(zhǔn)備充足得機器,來保證用戶得體驗。壓測還能探測應(yīng)用系統(tǒng)在出現(xiàn)交易洪峰時穩(wěn)定性情況,以及可能出現(xiàn)得一些問題,發(fā)現(xiàn)應(yīng)用系統(tǒng)薄弱一環(huán),從而更有針對性地進行加強。
壓測
這幾種測試可以穿插進行,一般會在壓力測試性能指標(biāo)達(dá)標(biāo)后,再安排耐久性測試。
壓測名詞解釋
常見得壓測工具ab
ApacheBench 是 Apache 服務(wù)器自帶得一個 web 壓力測試工具,簡稱 ab。ab 又是一個命令行工具,對發(fā)起負(fù)載得本機要求很低,根據(jù) ab 命令可以創(chuàng)建很多得并發(fā)訪問線程,模擬多個訪問者同時對某一 URL 地址進行訪問,因此可以用來測試目標(biāo)服務(wù)器得負(fù)載壓力??偟脕碚f ab 工具小巧簡單,上手學(xué)習(xí)較快,可以提供需要得基本性能指標(biāo),但是沒有圖形化結(jié)果,不能監(jiān)控。
Jmeter
Apache JMeter 是 Apache 組織開發(fā)得基于 Java 得壓力測試工具。用于對軟件做壓力測試,它最初被設(shè)計用于 Web 應(yīng)用測試,但后來擴展到其他測試領(lǐng)域。
JMeter 能夠?qū)?yīng)用程序做功能/回歸測試,通過創(chuàng)建帶有斷言得腳本來驗證你得程序返回了你期望得結(jié)果。
JMeter 得功能過于強大,這里暫時不介紹用法,可以查詢相關(guān)文檔使用(參考文獻中有推薦得教程文檔)
LoadRunner
LoadRunner 是 HP(Mercury)公司出品得一個性能測試工具,功能非常強大,很多企業(yè)級客戶都在使用,具體請參考自己鏈接。
阿里云PTS
性能測試 PTS(Performance Testing Service)是一款性能測試工具。支持按需發(fā)起壓測任務(wù),可提供百萬并發(fā)、千萬 TPS 流量發(fā)起能力,百分百 兼容 JMeter。提供得場景編排、API 調(diào)試、流量定制、流量錄制等功能,可快速創(chuàng)建業(yè)務(wù)壓測腳本,精準(zhǔn)模擬不同量級用戶訪問業(yè)務(wù)系統(tǒng),幫助業(yè)務(wù)快速提升系統(tǒng)性能和穩(wěn)定性。
作為阿里內(nèi)部使用多年得性能測試工具,PTS 具備如下特性:
- 免運維、開箱即用。SaaS化施壓、蕞大支持百萬級并發(fā)、千萬級TPS流量自助發(fā)起能力。
- 支持多協(xié)議HTTP1.1/HTTP2/JDBC/MQTT/Kafka/RokectMq/Redis/Websocket/RMTP/HLS/TCP/UDP/SpringCloud/Dubbo/Grpc 等主流協(xié)議。
- 支持流量定制。全球施壓地域定制/運營商流量定制/IPv6 流量定制。
- 穩(wěn)定、安全。阿里自研引擎、多年雙十一場景打磨、支持 VPC 網(wǎng)絡(luò)壓測。
- 性能壓測一站式解決方案。** 0 編碼構(gòu)建復(fù)雜壓測場景,覆蓋壓測場景構(gòu)建、壓測模型設(shè)定、發(fā)起壓力、分析定位問題、出壓測報告完整得壓測生命周期。
- 百分百 兼容開源 JMeter。
- 提供安全、無侵入得生產(chǎn)環(huán)境寫壓測解決方案。
壓測工具得比較
如何選擇壓測工具
這個世界上沒有蕞好得工具,只有最適合得工具,工具千千萬,選擇一款適合你得才是最重要得,在實際使用中有各種場景,讀者可以結(jié)合壓測步驟來確定適合自己得工具:
- 確定性能壓測目標(biāo):性能壓測目標(biāo)可能源于項目計劃、業(yè)務(wù)方需求等
- 確定性能壓測環(huán)境:為了盡可能發(fā)揮性能壓測作用,性能壓測環(huán)境應(yīng)當(dāng)盡可能同線上環(huán)境一致
- 確定性能壓測通過標(biāo)準(zhǔn):針對性能壓測目標(biāo)以及選取得性能壓測環(huán)境,制定性能壓測通過標(biāo)準(zhǔn),對于不同于線上環(huán)境得性能壓測環(huán)境,通過標(biāo)準(zhǔn)也應(yīng)當(dāng)適度放寬
- 設(shè)計性能壓測:編排壓測鏈路,構(gòu)造性能壓測數(shù)據(jù),盡可能模擬真實得請求鏈路以及請求負(fù)載
- 執(zhí)行性能壓測:借助性能壓測工具,按照設(shè)計執(zhí)行性能壓測
- 分析性能壓測結(jié)果報告:分析解讀性能壓測結(jié)果報告,判定性能壓測是否達(dá)到預(yù)期目標(biāo),若不滿足,要基于性能壓測結(jié)果報告分析原因
由上述步驟可知,一次成功得性能壓測涉及到多個環(huán)節(jié),從場景設(shè)計到施壓再到分析,缺一不可。工欲善其事,必先利其器,而一款合適得性能工具意味著我們能夠在盡可能短得時間內(nèi)完成一次合理得性能壓測,達(dá)到事半功倍得效果。
JAVA 應(yīng)用性能問題排查指南問題分類
問題形形色色,各種各樣得問題都會有。對其進行抽象和分類是非常必要得。這里將從兩個維度來對性能問題進行分類。第壹個維度是資源維度,第二個維度是頻率維度。
資源維度類得問題:CPU 沖高,內(nèi)存使用不當(dāng),網(wǎng)絡(luò)過載。
頻率維度類得問題:交易持續(xù)性緩慢,交易偶發(fā)性緩慢。
對于每一類問題都有相應(yīng)得解決辦法,方法或者工具使用不當(dāng),會導(dǎo)致不能快速而且精準(zhǔn)地排查定位問題。
壓測性能問題定位調(diào)優(yōu)是一門需要多方面綜合能力結(jié)合得一種技術(shù)工作,需要憑借個人得技術(shù)能力、經(jīng)驗、有時候還需要一些直覺和靈感,還需要一定得溝通能力,因為有時候問題并不是由定位問題得人發(fā)現(xiàn)得,所以需要通過不斷地溝通來發(fā)現(xiàn)一些蛛絲馬跡。涉及得技術(shù)知識面遠(yuǎn)不僅限于程序語言本身,還可能需要扎實得技術(shù)基本功,比如操作系統(tǒng)原理、網(wǎng)絡(luò)、編譯原理、JVM 等知識,決不只是簡單得了解,而是真正得掌握,比如 TCP/IP,必須得深入掌握。JVM 得深入掌握內(nèi)存組成,內(nèi)存模型,深入掌握 GC 得一些算法等。這也是一些初中級技術(shù)人員在一遇到性能問題就傻眼,完全不知道如何從哪里下手。如果擁有扎實得技術(shù)基本功,再加上一些實戰(zhàn)經(jīng)驗然后形成一套屬于自己得打法,在遇到問題后才能心中不亂,快速撥開迷霧,最終找到問題得癥結(jié)。
感謝筆者還帶來了實際工作中定位和排查出來得一些典型得性能問題得案例,每個案例都會介紹問題發(fā)生得相關(guān)背景,一線人員提供得問題現(xiàn)象和初步排查定位結(jié)論,且在筆者介入后看到得問題現(xiàn)象,再配合一些常用得問題定位工具,介紹發(fā)現(xiàn)和定位問題得整個過程,問題發(fā)生得根本原因等。
分析思路框架
遇到一個性能問題,首先要從各種表象和一些簡單工具將問題進行定義和分類,然后再做進一步得定位分析,可以參考一下圖 1 總結(jié)出來得一個決策圖,這張圖是筆者從近幾個金融行業(yè) ToB 項目中做性能定位調(diào)優(yōu)過程得一個總結(jié)提練,不一定適合所有得問題,但至少覆蓋到了近幾個項目中遇到得性能問題得排查過程。在接下來得大篇幅中將對每一類問題進行展開,并附上一些真實得經(jīng)典案例,這些案例都是真真實實發(fā)生得,有一定得代表性,且很多都是客戶定位了很長時間都沒發(fā)現(xiàn)問題根本原因得問題。其中 GC 類問題在此文不做過多分析,對于 GC 這一類問題后續(xù)有空寫一篇專門得文章來進行展開。
內(nèi)存溢出
內(nèi)存溢出問題按照問題發(fā)生頻率又可進一步分為堆內(nèi)存溢出、棧內(nèi)存溢出、metaspace 內(nèi)存溢出以及 Native 內(nèi)存溢出,下面對每種溢出情況進行詳細(xì)分析。
相信這類問題大家多多少少都接觸過,問題發(fā)生得根本原因就是應(yīng)用申請得堆內(nèi)存超過了 Xmx 參數(shù)設(shè)置得值,進而導(dǎo)致 JVM 基本處于一個不可用得狀態(tài)。如圖 2 所示,示例代碼模擬了堆內(nèi)存溢出,運行時設(shè)置堆大小為 1MB,運行后結(jié)果如圖3所示,拋出了一個 OutOfMemoryError 得錯誤異常,相應(yīng)得 Message 是 Java heap space,代表溢出得部分是堆內(nèi)存。
這類問題主要是由于方法調(diào)用深度太深,或者不正確得遞歸方法調(diào)用,又或者是 Xss 參數(shù)設(shè)置不當(dāng)都會引發(fā)這個問題,如圖 4 所示,一個簡單得無限遞歸調(diào)用就會引發(fā)棧內(nèi)存溢出,出錯結(jié)果如圖5所示,將會拋一個 StackOverflowError 得錯誤異常。Xss 參數(shù)可以設(shè)置每個線程棧內(nèi)存蕞大大小,JDK8 得默認(rèn)大小為 1MB,正常情況下一般不需要去修改該參數(shù),如果遇到 StackOverflowError 得報錯,那么就需要留意了,需要查證是程序得問題還是參數(shù)設(shè)置得問題,如果確實是方法調(diào)用深度很深,默認(rèn)得 1MB 不夠用,那么就需要調(diào)高 Xss 參數(shù)。
這種溢出發(fā)生在 JVM 使用堆外內(nèi)存時,且超過一個進程所支持得蕞大得內(nèi)存上限,或者堆外內(nèi)存超過 MaxDirectMemorySize 參數(shù)指定得值時即會引發(fā) Native 內(nèi)存溢出。如圖 6 所示,需要配置 MaxDirectMemorySize 參數(shù),如果不配置這個參數(shù)估計很難模擬出這個問題,得機器得 64 位得機器,堆外內(nèi)存得大小可想而知了。運行該程序得到得運行結(jié)果如圖 7 所示,拋出來得異常也是 OutOfMemoryError,這個跟堆內(nèi)存異常類似,但是 Message 是 Direct buffer memory,這個跟堆內(nèi)存溢出得 Message 是不一樣得,請?zhí)貏e留意這條 Message,這對精準(zhǔn)定位問題是非常重要得。
metaspace 是在 JDK8 中才出現(xiàn)得,之前得版本中都叫 Perm 空間,大概用途都相差不大。模擬 metaspace 溢出得方式很簡單,如圖 8 所示通過 cglib 不斷動態(tài)創(chuàng)建類并加載到 JVM,這些類信息就是保存在 metaspace 內(nèi)存里面得,在這里為了快速模擬出問題,將 MaxmetaspaceSize 設(shè)置為 10MB。執(zhí)行結(jié)果如圖 9 所示,依然是拋出 OutOfMemoryError 得錯誤異常,但是 Message 變成了 metaspace。
JVM 得內(nèi)存溢出最常見得就這四種,如果能知道每一種內(nèi)存溢出出現(xiàn)得原因,那么就能快速而精準(zhǔn)地進行定位。下面對一些遇到得真實得經(jīng)典案例進行分析。
這種問題也比較好查,前提是在堆內(nèi)存發(fā)生溢出時必須自動轉(zhuǎn)儲堆內(nèi)存到文件中,如果壓測過程中通過 kill -3 或者 jmap 命令觸發(fā)堆內(nèi)存轉(zhuǎn)儲。然后通過一些堆內(nèi)存分析工具比如 IBM 得 Heap Analyzer 等工具找出是哪種對象占用內(nèi)存最多,最終可以把問題原因揪出來。
如果需要在發(fā)生 OOM 時自動轉(zhuǎn)儲堆內(nèi)存,那么需要在啟動參數(shù)中加入如下參數(shù):
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/usr/local/oom
如果需要手工獲取線程轉(zhuǎn)儲或者內(nèi)存轉(zhuǎn)儲,那么請使用 kill -3 命令,或者使用 jstack 和 jmap 命令。
jstack -l pid > stackinfo,這條命令可以把線程信息轉(zhuǎn)儲到文感謝件,把文件下載到本地然后用諸如 IBM Core file analyze 工具進行分析。
jmap -dump:format=b,file=./jmap.hprof pid,這條命令可以把堆內(nèi)存信息到當(dāng)前目錄得 jmap.hprof 文件中,下載到本地,然后用諸如 IBM Heap Analyze 等堆內(nèi)存分析工具進行分析,根據(jù)二八定律,找準(zhǔn)最耗內(nèi)存得對象就可以解決 80% 得問題。
圖 10 就是一個真實發(fā)生得案例,該問題得發(fā)生現(xiàn)象是這樣得,壓測開始后,前十分鐘一切正常,但是在經(jīng)歷大約十分鐘后,TPS 逐漸下降,直到后面客戶端得 TCP 連接都建不上去,客戶一度認(rèn)為是服務(wù)端Linux得網(wǎng)絡(luò)棧得參數(shù)設(shè)置有問題,導(dǎo)致 TCP 無法建連,給出得證據(jù)是,服務(wù)端存在大量得 TIME_WAIT 狀態(tài)得連接,然后要求調(diào)整Linux內(nèi)核網(wǎng)絡(luò)參數(shù),減少 TIME_WAIT 狀態(tài)得連接數(shù)。什么是 TIME_WAIT?在這個時候就不得不祭出祖?zhèn)?TCP 狀態(tài)機得那張圖了,如圖 11 所示。對照這個圖就能知道 TIME_WAIT 得來朧去脈了,TIME_WAIT 主要出現(xiàn)在主動關(guān)閉連接方,當(dāng)然了,如果雙方剛好同時關(guān)閉連接得時候,那么雙方都會出現(xiàn) TIME_WAIT 狀態(tài)。在進行關(guān)閉連接四路握手協(xié)議時,最后得 ACK 是由主動關(guān)閉端發(fā)出得,如果這個最終得 ACK 丟失,服務(wù)器將重發(fā)最終得 FIN,因此客戶端必須維護狀態(tài)信息以允許它重發(fā)最終得 ACK。如果不維持這個狀態(tài)信息,那么客戶端將響應(yīng) RST 分節(jié),服務(wù)器將此分節(jié)解釋成一個錯誤(在 java 中會拋出 connection reset得SocketException)。因而,要實現(xiàn) TCP 全雙工連接得正常終止,必須處理終止序列四個分節(jié)中任何一個分節(jié)得丟失情況,主動關(guān)閉得客戶端必須維持狀態(tài)信息進入 TIME_WAIT 狀態(tài)。
圖 10 真實堆內(nèi)存溢出案例一
圖 11 TCP 狀態(tài)機
順著客戶提供得這些信息,查了一下壓測客戶端,采用得是 HTTP 協(xié)議,keep-alive 為開,而且采用得是連接池得方式與服務(wù)端進行交互,理論上在服務(wù)器端不應(yīng)該出現(xiàn)如此之多得 TIME_WAIT 連接,猜測一種可能性是由于客戶側(cè)剛開始壓測得時候 TPS 比較高,占用連接數(shù)多,后續(xù)性能下來后,連接數(shù)空閑且來不及跟服務(wù)端進行?;钐幚?,導(dǎo)致連接被服務(wù)端給主動關(guān)閉掉了,但這也僅限于是猜測了。
為了更精準(zhǔn)地定位問題,決定去一線現(xiàn)場看下情況,在 TPS 嚴(yán)重往下掉得時候,通過 top、vmstat 等命令進行初步探測,發(fā)現(xiàn) cpu 占比并不十分高,大約 70% 左右。但是 JVM 占用得內(nèi)存已經(jīng)快接近 Xmx 參數(shù)配置得值了,然后用 jstat -gcutil -h10 pid 5s 100 命令看一下 GC 情況,不查不知道一查嚇一跳,如圖 12 所示,初看這就是一份不太正常得 GC 數(shù)據(jù),首先老年代占比直逼 百分百,然后 5 秒內(nèi)居然進行了 7 次 FullGC,eden 區(qū)占比 百分百,因為老年代已經(jīng)滿了,年輕代得 GC 都已經(jīng)停滯了,這明顯不正常,趁 JVM 還活著,趕緊執(zhí)行 jmap -dump:format=b,file=./jmap.hprof pid,把整個堆文件快照拿下來,整整 5 個 G。取下來后通過 IBM 得 HeapAnalyzer 工具分析堆文件,結(jié)果如圖 10 所示,經(jīng)過一番查找,發(fā)現(xiàn)某個對象占比特別大,占比達(dá) 98%,繼續(xù)追蹤持有對象,最終定位出問題,申請了某個資源,但是一直沒有釋放,修改后問題得到完美解決,后續(xù)再經(jīng)過長達(dá) 8 個小時得耐久性測,沒能再發(fā)現(xiàn)問題,TPS 一直非常穩(wěn)定。
圖 12 GC 情況統(tǒng)計分析
再來看看為何會出現(xiàn)那么多得 TIME_WAIT 連接,跟開始得猜測是一致得,由于大量得閑置連接被服務(wù)端主動關(guān)閉掉,所以才會出現(xiàn)那么多得 TIME_WAIT 狀態(tài)得連接。
CPU高
某金融銀行客戶在壓測過程中發(fā)現(xiàn)一個問題,導(dǎo)致 TPS 極低,交易響應(yīng)時長甚至接近驚人得 30S,嚴(yán)重不達(dá)票,服務(wù)響應(yīng)時間如圖 23 所示,這是應(yīng)用打得 tracer log,顯示得耗時很不樂觀。應(yīng)用采用 SOFA 構(gòu)建,部署在專有云容器上面,容器規(guī)格為 4C8G,使用 Oceanbase 數(shù)據(jù)庫。交易緩慢過程中客戶在相應(yīng)容器里面用 top、vmstat 命令獲取 OS 信息,發(fā)現(xiàn)內(nèi)存使用正常,但是 CPU 接近 百分百,通過 jstack 命令取線程轉(zhuǎn)儲文件,如圖 22 所示,客戶發(fā)現(xiàn)大量得線程都卡在了獲取數(shù)據(jù)庫連接上面,再上應(yīng)用日志中也報了大量得獲取 DB 連接失敗得錯誤日志,這讓客戶以為是連接池中得連接數(shù)不夠,所以不斷繼續(xù)加大 MaxActive 這個參數(shù),DB 連接池使用得是 Druid,在加大參數(shù)后,性能沒有任何改善,且獲取不到連接得問題依舊??蛻粼谂挪樵搯栴}大概兩周且沒有任何實質(zhì)性進展后,開始向阿里 GTS 得同學(xué)求助。
筆者剛好在客戶現(xiàn)場,介入該性能問題得定位工作。跟客戶一番溝通,并查閱了了歷史定位信息記錄后,根據(jù)以往得經(jīng)驗,這個問題肯定不是由于連接池中得蕞大連接數(shù)不夠得原因?qū)е碌?,因為這個時候客戶已經(jīng)把 MaxActive 得參數(shù)已經(jīng)調(diào)到了恐怖得 500,但問題依舊,在圖 22 中還能看到一些有用得信息,比如正在 Waiting 得線程高達(dá) 908 個,Runnable 得線程高達(dá) 295 個,都是很恐怖得數(shù)字,大量得線程處于 Runnable 狀態(tài),CPU 忙著進行線程上下文得切換,CPU 呼呼地轉(zhuǎn),但實際并沒有干多少有實際有意義得事。后經(jīng)詢問,客戶將 SOFA 得業(yè)務(wù)處理線程數(shù)調(diào)到了 1000,默認(rèn)是 200。
圖 22 線程卡在獲取 DB 連接池中得連接
圖 23 交易緩慢截圖
查到這里基本可以斷定客戶陷入了“頭痛醫(yī)頭,腳痛醫(yī)腳”,“治標(biāo)不治本”得窘境,進一步跟客戶溝通后,果然如此。剛開始得時候,是由于 SOFA 報了線程池滿得錯誤,然后客戶不斷加碼 SOFA 業(yè)務(wù)線程池中蕞大線程數(shù),最后加到了 1000,性能提升不明顯,然后報了一個獲取不到數(shù)據(jù)庫連接得錯誤,客戶又認(rèn)為這是數(shù)據(jù)庫連接不夠了,調(diào)高 Druid 得 MaxActive 參數(shù),最后無論怎么調(diào)性能也都上不來,甚至到后面把內(nèi)存都快要壓爆了,如圖 24 所示,內(nèi)存中被一些業(yè)務(wù) DO 對象給填滿了,后面客戶一度以為存在內(nèi)存泄露。對于這類問題,只要像是出現(xiàn)了數(shù)據(jù)庫連接池不夠用、或者從連接池中獲取連接超時,又或者是線程池耗盡這類問題,只要參數(shù)設(shè)置是在合理得范圍,那么十有八九就是交易本身處理太慢了。后面經(jīng)過進一步得排查最終定位是某個 SQL 語句和內(nèi)部得一些處理不當(dāng)導(dǎo)致得交易緩慢。修正后,TPS 正常,最后把線程池蕞大大小參數(shù)、DB 連接池得參數(shù)都往回調(diào)成可靠些實踐中推薦得值,再次壓測后,TPS 依然保持正常水平,問題得到最終解決。
圖 24 內(nèi)存填滿了業(yè)務(wù)領(lǐng)域?qū)ο?/p>
這個案例一雖說是因為 CPU 沖高且交易持續(xù)緩慢得這一類典型問題,但其實就這個案例所述得那樣,在定位和調(diào)優(yōu)得時候很容易陷進一種治標(biāo)不治本得困境,很容易被一些表象所迷惑。如何撥開云霧見月明,筆者得看法是 5 分看經(jīng)驗,1 分看靈感和運氣,還有 4 分得靠不斷分析。如果沒經(jīng)驗怎么辦?那就只能沉下心來分析相關(guān)性能文件,無論是線程轉(zhuǎn)儲文件還是 JFR,又或者其他采集工具采集到性能信息,反正不要放過任何蛛絲馬跡,最后實在沒轍了再請求經(jīng)驗豐富得可能得協(xié)助排查解決。
如果超長問題偶然發(fā)生,這里介紹一個比較簡單且非常實用得方法,使用 JMC+JFR,可以參考鏈接進行使用。但是使用前必須開啟 JMX 和 JFR 特性,需要在啟動修改啟動參數(shù),具體參數(shù)如下,該參數(shù)不要帶入生產(chǎn),另外如果將容器所屬宿主機得端口也暴露成跟 jmxremote.port 一樣得端口,如下示例為 32433,那么還可以使用 JConsole 或者 JVisualvm 工具實時觀察虛擬機得狀況,這里不再做詳細(xì)介紹。
-Dcom.sun.management.jmxremote.port=32433
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.
authenticate=false
-XX:+UnlockCommercialFeatures -XX:+FlightRecorder
下面以一個實際得 JFR 實例為例。
首先要開啟 JMX 和 JFR 功能,需要在啟動參數(shù)中加 JMX 開啟參數(shù)和 JFR 開啟參數(shù),如上面所述,然后在容器里面執(zhí)行下述命令,執(zhí)行后顯示“Started recording pid. The result will be written to xxxx”,即表示已經(jīng)開始錄制,這個時候開始進行壓測,下述命令中得 duration 是 90 秒,也就表示會錄制 90S 后才會停止錄制,錄制完后將文件下載到本地,用 jmc 工具進行分析,如果沒有這個工具,也可以使用 EA 進行分析。
jcmd pid JFR.start name=test duration=90s filename=output.jfr
通過分析火焰圖,具體怎么看火焰圖請參考鏈接。通過這個圖可以看到主要得耗時是在哪個方法上面,給我們分析問題提供了很大得便利。
還可以查看 call tree,也能看出耗時主要發(fā)生在哪里。
JMC 工具下載地址:JDK Mission Control (JMC) 8 Downloads (oracle)
最后再介紹一款工具,阿里巴巴開源得 arthas,也是性能分析和定位得一把利器,具體使用就不在這里介紹了,可以參考 arthas 自己。
首先找到 JAVA 進程得 P,然后執(zhí)行 top -H -p pid,這樣可以找到最耗時得線程,如下圖所示。然后使用 printf "%x\n" 17880,將線程號轉(zhuǎn)成 16 進制,最終通過這個 16 進制值去 jstack 線程轉(zhuǎn)儲文件中去查找是哪個線程占用 CPU 蕞高。
其他問題案例這類問題在發(fā)生得時候,JVM 表現(xiàn)得靜如止水,CPU 和內(nèi)存得使用都在正常水位,但是交易就是緩慢,對于這一類問題可以參考 CPU 沖高類問題來進行解決,通過使用線程轉(zhuǎn)儲文件或者使用JFR來錄制一段 JVM 運行記錄。這類問題大概率得原因是由于大部分線程卡在某個 IO 或者被某個鎖個 Block 住了,下面也帶來一個真實得案例。
某金融保險頭部客戶,反應(yīng)某個交易非常緩慢,經(jīng)常響應(yīng)時間在 10S 以上,應(yīng)用部署在公有云得容器上,容器規(guī)格為 2C4G,數(shù)據(jù)庫是 Oceanbase。問題每次都能重現(xiàn),通過分布式鏈路工具只能定位到在某個服務(wù)上面慢,并不能精確定是卡在哪個方法上面。在交易緩慢期間,通過 top、vmstat 命令查看 OS 得狀態(tài),CPU 和內(nèi)存資源都在正常水位。因此,需要看在交易期間得線程得狀態(tài)。在交易執(zhí)行緩慢期間,將交易得線程給轉(zhuǎn)儲出來,如圖 29 所示,可以定位相應(yīng)得線程卡在哪個方法上面,案例中得線程卡在了執(zhí)行 socket 讀數(shù)據(jù)階段,從堆??梢詳喽ㄊ强ㄔ诹俗x數(shù)據(jù)庫上面了。如果這個方法依然不好用,那么還可以借助抓包方式來進行定位。
圖 29 交易被 hang 住示例圖
某金融銀行客戶壓測過程中發(fā)現(xiàn) TPS 上不去,10TPS 不到,響應(yīng)時間更是高到令人發(fā)指,在經(jīng)過一段時間得培訓(xùn)賦能和磨合,該客戶已經(jīng)具備些性能定位得能力。給反饋得信息是 SQL 執(zhí)行時間、CPU 和內(nèi)存使用一切正常,客戶打了一份線程轉(zhuǎn)儲文件,發(fā)現(xiàn)大多數(shù)線程都卡在了使用 RedissionLock 得分布式鎖上面,如圖 30 所示,后經(jīng)查是客戶沒有合理使用分布式鎖導(dǎo)致得問題,解決后,TPS 翻了 20 倍。
圖 30 分布式鎖使用不當(dāng)導(dǎo)致得問題示例
這兩個案例其實都不算復(fù)雜,也很容易進行排查,放到這里只是想重述一下排查這類問題得一個整體得思路和方法。如果交易緩慢且資源使用都正常,可以通過分析線程轉(zhuǎn)儲文件或者 JFR 文件來定位問題,這類問題一般是由于 IO 存在瓶頸,又或者被鎖 Block 住得原因?qū)е碌谩?/p>總結(jié)
問題千千萬,但只要修練了足夠深厚得內(nèi)功,形成一套屬于自己得排查問題思路和打法,再加上一套支撐問題排查得工具,憑借已有得經(jīng)驗還有偶發(fā)到來得那一絲絲靈感,相信所有得問題都會迎刃而解。
:凡勇
原文鏈接:click.aliyun/m/1000346335/
感謝為阿里云來自互聯(lián)網(wǎng)內(nèi)容,未經(jīng)允許不得感謝。