jstack调试_增压的jstack:如何以100mph的速度调试服务器
jstack調試
使用jstack調試實時Java生產服務器的指南
jstack就像U2一樣-從時間的黎明就一直在我們身邊,我們似乎無法擺脫它 。 除了笑話,到目前為止,jstack是您的工具庫中用于調試實時生產服務器的最方便的工具之一。 即便如此,我仍然覺得它在情況惡化時能夠將您從火中撲滅的能力仍未得到充分利用,因此我想分享一些方法,您可以在對抗生產錯誤的戰爭中將其增強為更強大的武器。
jstack的核心是一個超級簡單的工具,它向您顯示在目標JVM中運行的所有Java線程的堆棧跟蹤。 只需通過一個pid將其指向JVM進程即可獲得該時間點所有線程堆棧跟蹤的打印輸出。 這使您能夠回答“這個服務器在做什么?”這個古老的問題,并使您更進一步地了解它為什么真正在做它。 關于jstack的最大優點是它輕巧-它不會給JVM增加任何性能開銷,也不會更改其執行狀態(與調試器或探查器不同)。
由于沒有什么是完美的 ,因此jstack有兩個明顯的缺點。 第一個是jstack除了調用堆棧外沒有為您提供任何變量狀態,這意味著盡管您可能正在查看堆棧,但您不知道將其置于何處的狀態。 一個很好的例子是查看掛起的JVM,其中jstack會向您顯示大量線程正在執行數據庫查詢或等待獲得連接。
這可能意味著某些查詢執行時間太長,導致其他線程要么等待連接,要么被拒絕。 在這里,您真的想知道正在執行哪個查詢(或其查詢參數是什么),這會導致速度降低以及何時開始。 當然,在很多情況下,這只是一個示例,其中某些線程被阻塞并降低了應用程序的吞吐量。 但是不幸的是,使用jstack時,因為您沒有獲得任何變量狀態,所以您無法真正確定應歸咎于哪個線程。 還是可以嗎?
jstack的第二個缺點是它不是永遠在線的工具。 這意味著當問題發生時,您必須在那里-在生產中這是罕見的事件。 在不斷重啟VM的彈性環境中,情況更是如此。
好的部分到了–讓我們看一下兩種技術,它們可以幫助我們克服這兩個缺點,并使一個好的工具真正變得很棒。
創建有狀態線程數據
第一個問題是如何向jstack打印輸出添加狀態? 答案簡單而強大-線程名稱。 盡管許多人錯誤地認為線程名稱是不可變的,或者是操作系統確定的屬性,但實際上,每個線程具有可變的且非常重要的特征。 這也是進入您的jstack流的關鍵所在。
實際的應用程序就像日志記錄一樣,一旦它通過諸如servlet,actor或Scheduler之類的入口點輸入代碼,就應該控制線程名。 此時,您需要將其名稱設置為一個有意義的值,該值可以幫助您了解執行上下文和相關參數,從而可以幫助您隔離事務及其內容。
這很可能包括–
這些數據將意味著查看打印輸出(例如下面的打印輸出實際上并不能告訴我們任何線程在做什么或為什么)與提供信息的輸出之間的區別:
Object.wait()中的“ pool-1-thread-1”#17 prio = 5 os_prio = 31 tid = 0x00007f9d620c9800 nid = 0x6d03 [0x000000013ebcc000]
將此與以下線程打印輸出進行比較:
“隊列處理線程,MessageID:AB5CAD,類型:AnalyzeGraph,隊列:ACTIVE_PROD,Transaction_ID:5678956,開始時間:2014/10/8 18:34”
#17 prio = 5 os_prio = 31 tid = 0x00007f9d620c9800 nid = 0x6d03 in Object.wait()[0x000000013ebcc000]
您在這里看到的是對該線程實際作用的更全面的解釋。 您可以從AWS隊列中輕松查看其出隊消息,它正在分析該消息,其類型,ID和交易ID。 最后,但并非最不重要–線程何時開始對其進行處理。 這可以幫助您非常快速地將注意力集中在那些被卡住的線程上,并查看它們所處的狀態。從那以后,在本地進行優化和重現變得更加容易。
這里的替代方案是要么希望日志文件中有數據,而且能夠將日志中的數據關聯到該確切線程。 另一種選擇是在生產環境中本地或遠程連接調試器。 既不是很愉快又很費時間。
在線程名稱中寫入此信息也有助于傳統日志記錄。 即使大多數日志記錄框架提供了可以添加到日志中的基于線程的上下文,您也必須確保正確配置它。 使用線程名稱還可以確保您將在日志中擁有所需的所有數據。
注意:某些人可能會說線程名稱不可調整或更改。 從我多年的個人經驗以及許多同事的經驗來看,我是一個很小的信徒。
使jstack始終在線
當使用jstack時,我們面臨的第二個挑戰是,就像調試器一樣,它是在問題發生時必須手動操作以捕獲損壞狀態的一種工具。 但是,當服務器掛起或低于或低于某個特定閾值時,有一種更活躍的方法可以使用jstack自動生成打印輸出。 關鍵是要以編程方式調用jstack,就像在滿足特定應用程序條件時從JVM中的任何日志記錄功能一樣。
這里的兩個主要挑戰是何時以及如何實現。
如何以編程方式激活jstack?
由于jstack是一個普通的OS進程,因此調用它非常簡單。 您要做的就是激活jstack進程并將其指向您自己。 這里的關鍵是如何從JVM中獲取進程的pid。 實際上,沒有標準的Java API可以做到這一點( 至少要等到Java 9才能實現 )。 這是完成工作的一小段代碼(盡管不是文檔api的一部分):
String mxName = ManagementFactory.getRuntimeMXBean().getName();int index = mxName.indexOf(PID_SEPERATOR);String result;if (index != -1) {result = mxName.substring(0, index); } else {throw new IllegalStateException("Could not acquire pid using " + mxName); }另一個小挑戰是將jstack輸出定向到您的日志中。 使用輸出流檢流器也很容易設置。 在此處查看有關如何將您調用的進程輸出的輸出數據定向到日志文件或輸出流中的示例。
盡管可以使用getAllStackTraces在內部捕獲正在運行的線程的堆棧跟蹤,但出于多種原因,我更喜歡通過運行jstack來執行此操作。 首先,這是我通常希望在正在運行的應用程序外部進行的操作(即使JVM正在參與提供信息),以確保我不會通過進行內省性調用來影響應用程序的穩定性。 另一個原因是,jstack在功能方面更為強大,例如向您顯示本機框架和鎖定狀態,而這在JVM中是不可用的。
什么時候激活jstack?
您需要做出的第二個決定是在什么條件下您希望JVM記錄jstack。 當服務器低于或高于特定處理(即請求或消息處理)閾值時,可能會在預熱期之后執行此操作。 您可能還需要確保每次激活之間都花費足夠的時間。 只是為了確保您不會在低負載或高負載下泛濫日志。
您將在此處使用的模式是從JVM中加載看門狗線程,該線程可以定期查看應用程序的吞吐量狀態(例如,最近兩分鐘內處理的消息數),并確定是否對屏幕進行“截屏”。線程狀態將很有幫助,在這種情況下,它將激活jstack并將其記錄到文件中。
設置此線程的名稱以包含目標和實際吞吐量狀態,因此當您執行自動jstack快照時,您可以確切地看到看門狗線程決定這樣做的原因。 由于這只會每隔幾分鐘發生一次,因此該過程沒有實際的性能開銷,尤其是與所提供的數據質量相比。
以下是顯示此模式的片段。 每當處理一條消息時,startScheduleTask加載一個看門狗線程以定期檢查吞吐量值,該吞吐量值使用Java 8 并發加法器遞增。
public void startScheduleTask() {scheduler.scheduleAtFixedRate(new Runnable() {public void run() {checkThroughput();}}, APP_WARMUP, POLLING_CYCLE, TimeUnit.SECONDS); }private void checkThroughput() {int throughput = adder.intValue(); //the adder in inc’d when a message is processedif (throughput < MIN_THROUGHPUT) {Thread.currentThread().setName("Throughput jstack thread: " + throughput);System.err.println("Minimal throughput failed: exexuting jstack");executeJstack(); //see the code on github to see how this is done}adder.reset(); }- 在此處可以找到從代碼中搶先調用jstack的完整源代碼。
翻譯自: https://www.javacodegeeks.com/2014/10/supercharged-jstack-how-to-debug-your-servers-at-100mph.html
jstack調試
總結
以上是生活随笔為你收集整理的jstack调试_增压的jstack:如何以100mph的速度调试服务器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 彼岸花怎么画 彼岸花简笔画教程
- 下一篇: mecity是什么牌子 mecity牌子