引言
WebSphere 應用服務的故障診斷一直是客戶最為關心的問題之一。為了對 WebSphere 應用 服務器進行快速的問題診斷, IBM 提供了廣泛的支持,包括幫助客戶進行快速的數據采集,指 導客戶進行數據分析以及提供大量的參考手冊和技術文檔等,這些努力大大減輕了問題診斷的 工作量。然而,問題的解決最終還是要依賴於用戶對具體問題的分析。這就像是醫生給病人看 病,任何先進的醫療器械和理論知識都代替不了醫生的診斷,為了給病人治病,醫生不僅要有 扎實的理論基礎,還要有豐富的實踐經驗,對 WebSphere 應用服務器進行問題診斷也是一樣。 那麼怎樣才能獲得故障診斷相關的經驗呢? 在實踐中學習當然是我們獲得經驗的最佳途徑,但 僅僅依靠在工作中解決實際問題來獲得經驗將是一個漫長的過程,並且通常都需要付出一定的 代價。與之相比,另一種主動的方式 – 實驗 – 要劃算的多,通過實驗的方式獲得經驗不僅 可以幫助我們在問題發生時進行快速的診斷,而且還有可能因為這些已有的經驗避免一些問題 的發生。本文將介紹一種進行問題診斷的實驗工具 – Problem Diagnostics Lab Toolkit (http://www.alphaworks.ibm.com/tech/pdtk), 它可以幫我們快速重現問題,並且為問題 的定位和診斷提供指導。
什麼是 Problem Diagnostics Lab Toolkit ?
Problem Diagnostics Lab Toolkit (PDLT) 是一個安裝在 WebSphere Application Server 上的企業應用程序,與普通的應用程序相比,它的特點是可以動態的修改需要運行 Java 代碼 ,這些 Java 代碼是通過應用程序寫到 jsp 中的,所以當用戶在浏覽器中修改相應的代碼以後 ,不需要重起應用程序,就能立即執行新的代碼。包含 Java 代碼的 jsp 可以通過 "動作面板 " 中的按鈕來調用的 , 這樣無論是修改還是運行 Java 代碼都非常的方便。監控模塊可以幫助 我們察看系統當前的狀態,包括線程狀態、內存和 CPU 使用情況,以及不同請求的響應時間等 。 PDLT 還包含一個內置的壓力引擎,當我們需要重現一些在壓力條件下才能重現的問題時, 可以設置模擬的客戶端的個數、每個客戶端的請求數以及兩次請求之間的時間間隔。下圖展示 了 PDLT 的主要功能模塊:
圖 1. PDLT 的功能模塊
管理模塊: 主要負責實驗案例的管理和維護。
代碼編輯器: 動態修改 Java 代碼,修改後的 Java 代碼可以立即被執行,而不需要重新 部署應用程序。
監視模塊: 監視應用程序的運行狀態,比如內存、CPU、線程和平均響應時間等。
壓力引擎: 模擬多客戶端對 Java 代碼(JSP) 進行訪問。
案例庫: 存放的案例。
Problem Diagnostics Lab Toolkit 的安裝
PDLT 是一個的企業應用程序,用戶需要將其部署在 WebSphere 的單 Server 環境中,部署 過程中只需要接受默認的配置即可,不需要配置額外的資源和環境變量。
具體安裝步驟如下:
1) 從 http://www.alphaworks.ibm.com/tech/pdtk/download 下載 PDLT。
2) 啟動 WAS,並打開 admin console 。
3) 選擇“應用程序”->“安裝新的應用程序”。
4) 選擇壓縮包中的 ear 文件。
5) 全部使用默認選項安裝該 ear 。
安裝完成後,用戶可以通過浏覽器訪問 http://hostname:port/LabToolkit,其界面如下圖 所示
圖 2. PDLT 的界面
問題 (Problem): 是對實驗場景的分類,每一類問題可以通過多個場景來進行重現。
場景 (Scenario): 用於重現問題的實驗場景,每一個場景包含一個向導和一個動作面板。
向導(Wizard): 指導用戶完成場景中的步驟。
動作面板 (Action Pane): 包括一系列動作按鈕,每個動作按鈕對應後台的一個 jsp 文件 ,用戶可以執行或者修改這個 jsp 文件中的代碼。
監視器 (Monitor): 用戶監控系統的狀態。
消息面板 (Message Console): 用於顯示運行過程中的日志消息。
Problem Diagnostics Lab Toolkit 的使用
了解試驗場景
每一個試驗場景代表一種可能發生問題的情況,用戶首先選擇一個問題類別,然後在該類別 中選擇一個試驗場景,這裡我們以死鎖問題為例,選擇 “ThreadHang" --> "DeadLock",
這時我們可以看到這個實驗場景的向導(圖 3)和動作面板(右側),向導主要分為四個步 驟 :
簡介 (Instruction): 主要介紹該場景所要重現的問題。
問題重現 (Reproduction):介紹重現該場景的步驟和注意事項。
分析問題 (Investigation): 指導用戶進行問題診斷。
總結 (Summary): 問題總結。
通過鼠標右鍵彈出的上下文菜單,用戶可以增加或者刪除向導的步驟或者對向導中的內容進 行修改。
圖 3. 向導部分
圖 4. 動作面板
查看代碼
"Dead Lock" 的動作面板中有兩個動作按鈕,"DeadLock Jsp" 按鈕和 "Correct Jsp"。
用鼠標右鍵單擊 "DeadLock Jsp" 按鈕,在快捷菜單中選擇 "Edit Action Button" ,在彈 出的 Java 編輯器中我們可以查看或者編輯這個按鈕所執行的 Java 代碼,其代碼如下:
清單 1.“DeadLock Jsp” 按鈕執行的 Java 代碼
synchronized (lock1) { // lock1 is defined in the "Methods and Static Variables" tab
Thread.sleep(5000);
ThreadMonitor.registerThreadStatus("blocked");
//It will be blocked here if the thread can not get the lock2
synchronized (lock2) {
ThreadMonitor.registerThreadStatus("running");
//It will continue to run if the thread can get the lock2
}
}
synchronized (lock2) { // lock2 is defined in the "Methods and Static Variables" tab
Thread.sleep(5000);
ThreadMonitor.registerThreadStatus("blocked");
//It will be blocked here if the thread can not get the lock1
synchronized (lock1) {
ThreadMonitor.registerThreadStatus("running");
//It will continue to run if the thread can get the lock1
}
}
圖 5. 代碼編輯器
這段代碼主要完成如下操作:
獲取一個全局鎖 locker1
(Sleep(5000))
獲取一個全局鎖 locker2
釋放全局鎖 locker2
釋放全局鎖 locker1
獲取一個全局鎖 locker2
(Sleep(5000))
獲取一個全局鎖 locker1
釋放全局鎖 locker1
釋放全局鎖 locker2
這段代碼在單線程運行時可以很順利的執行,但在多線程並發條件下卻很容易發生死鎖:當 兩個不同的線程分別執行到第 2 步和第 6 步之前時 , 其中的一個線程已經占用了 locker1, 它需要等待 locker2, 而另一個線程則剛好相反。 因此如果我們模擬多用戶並發執行這段代碼 ,就能夠重現死鎖問題。
模擬多用戶並發
PDLT 內置了壓力引擎,可以方便的模擬多用戶並發調用的場景,如下圖所示,展開動作面 板下方的 Advanced Settings"面板,可以設置模擬客戶端的個數,發送請求總數以及請求之間 的間隔時間。這裡我們將用戶數設置為 2。返回動作面板並單擊"DeadLock Jsp" 按鈕,壓力引 擎會同時發送 2 個請求來調用"DeadLock Jsp" 按鈕所對應 jsp,從而運行上面我們所編輯的 代碼。
圖 6. 設置壓力引擎
監控線程運行情況
展開“Monitors”面板,可以看到 3 個 tab 頁, 分別用來監控線程信息、內存和 cpu 使 用情況以及請求的平均響應時間。 這裡我們主要關心線程的運行情況,從線程信息頁我們可以 看到剛剛運行的兩個新的線程都處於“block”狀態,並且這種狀態會一直持續下去。 這就是 死鎖,它不但導致發生死鎖的兩個線程無法正常結束,這兩個線程所占用的資源還會影響到更 多的線程, 當線程總數超過 Web Container 線程池的最大線程數時,則所有的請求都會被拒 絕。
圖 7. 監控運行情況
察看正確代碼
用鼠標右鍵單擊 "Correct Jsp" 按鈕,在快捷菜單中選擇 "Edit Action Button",在彈出 的 Java 編輯器中我們可以查看或者編輯這個按鈕所執行的 Java 代碼,其代碼如下:
清單 2.“Correct Jsp” 按鈕執行的 Java 代碼
synchronized (lock1) { // lock1 is defined in the "Methods and Static Variables" tab
Thread.sleep(5000);
ThreadMonitor.registerThreadStatus("blocked");
synchronized (lock2) {
ThreadMonitor.registerThreadStatus("running");
}
}
synchronized (lock1) { // lock2 is defined in the "Methods and Static Variables" tab
Thread.sleep(5000);
ThreadMonitor.registerThreadStatus("blocked");
synchronized (lock2) {
ThreadMonitor.registerThreadStatus("running");
}
}
這段代碼主要完成如下操作:
獲取一個全局鎖 locker1
(Sleep(5000))
獲取一個全局鎖 locker2
釋放全局鎖 locker2
釋放全局鎖 locker1
獲取一個全局鎖 locker1
(Sleep(5000))
獲取一個全局鎖 locker2
釋放全局鎖 locker2
釋放全局鎖 locker1
與 “DeadLock Jsp” 相比,我們在這段代碼中僅僅調整了一下 locker1 和 locker2 的嵌 套順序,但當我們用兩個(或者更多)客戶端同時發出請求時,所有線程都能夠正常結束。
因此,在多線程環境下,一定要保證鎖的嵌套順序是一致的,這樣才能避免死鎖的發生。
結束語
除了死鎖問題之外,PDLT 還包含很多其他常見的典型問題,比如 Java 內存溢出、本地內 存溢出、CPU 使用率過高、連接洩漏等。 對於這些常見的錯誤,用戶最好在問題發生之前就對 它進行了解,了解這些問題發生時系統的症狀,以及如何診斷和解決這類問題。這樣當真正的 問題到來的時候才能迅速做出判斷,從而減少由於系統中斷而帶來的損失。