如何診斷 J2EE 系統中的性能問題
時間:2004-12-14 作者:John Bley 瀏覽次數: 本文關鍵字:診斷, J2EE, 性能, 測試, 瓶頸 |
|
爲了正確地找到問題所在,您需要對症狀有全面瞭解,要做好準備進行大量研究工作,最後您需要制定正確的治療方案。本文討論了 J2EE 應用程序性能問題的一些最常見類型和它們產生的原因,以及如何正確地診斷和消除它們的推薦指導原則。
症狀
WebLogic 應用程序性能問題的症狀是什麼?您看到的症狀可以指導您在所有可能的問題中進行搜索。請準備一個筆記本並開始向人們調查。試着把對問題根本原因的推測和假設與系統行爲的實際證據分離。下面是一個常見症狀集的列表:
- 持續緩慢:應用程序一直特別慢。改變環境因素(負載、數據庫連接數量)對整體響應時間的改變很小。
- 隨着時間推進越來越慢:系統運行時間越長(負載相對均衡不變的情況下),就變得越慢。有可能是(最後)達到了某個閾值,系統被鎖定或者由於大量錯誤而崩潰。
- 隨着負載增加越來越慢:每增加一個額外用戶,應用程序就變得越慢。如果用戶離開系統,系統就“冷卻下來”,恢復正常。
- 零星的掛起或者異常錯誤:偶爾(可能由於負載或某些其它原因),在頁面無法完成或者出現追蹤到異常和堆棧的錯誤頁時,用戶會看到掛起的情況。掛起的數量可能不同,但總是無法完全消除,甚至在強化 (“burn in”) 期間之後也是如此。
- 可以預見的鎖定:首先出現一些掛起或錯誤,然後加速出現,直到系統完全被鎖定。通常這些問題可以通過“重新啓動來管理”的方式解決。
- 突然混亂:系統一直運行正常,相當一段時間裏(可能一個小時,也可能是三天)性能都還差強人意,但是“突然某個時候,根本沒有任何原因的”,系統開始出現大量錯誤,或者被鎖定。
爲什麼問題診斷如此複雜?
對於 WebLogic 應用程序的某個具體應用模式來說,沒有既定的公式可以用來推導出它的性能(在嚴格的實時工程當中,速度單調分析這類技術確實可以做這項工作,但是在本文裏 還是讓我們忘記它吧)。網絡上是否存在另外一個系統正在密集使用一個共享的後端服務,對於實際產生的性能有很大影響。性能也許還取決於 JDBC 驅動程序版本和數據庫的正確匹配。也許開發人員三年前寫的一些代碼恰巧在這個時候纔出現特定類型的異常,而您急切需要的解決問題回饋,卻恰恰包含在這個異 常裏。
從本質上說,典型業務系統的性能是由成千上萬交互的變量和決策共同作用的結果。就像人體一樣,有太多連鎖着的部分和過程,所以很難理解系統的整體。因此我們進行了簡化,並求助於拱形模式。
疾病
您看到的症狀的根本原因是什麼?它是初級流行性感冒或者是肺炎的開始嗎?是應用程序內部的底層問題還是它所在的 JVM 外部的問題?請參閱表 1 中應用程序性能低下的一些最常見原因。
表1
測量關鍵的統計指標
當您負責診斷問題的時候,您應當能夠跟蹤關於您的 WebLogic 應用程序健康情況的關鍵統計指標。您能測量什麼?有什麼工具可以提供幫助呢?
- 使用的全部內存:在不同級別上 (JVM 堆棧,操作系統),Java 堆棧 profiler 對堆棧的正確使用提供了可見性;像 top ,vmstat 以及 Windows Perfmon 這樣的工具在操作系統級別上爲內存使用提供可見性。察看 Java 堆棧的一個簡單的聚合視圖,請打開―verbose:gc 開關(如果在您的 JVM 上可以使用的話)。
- CPU 時間:合併(可以通過使用 top 等方式得到)每個組件或每種方法。其中某些指標可以通過 WebLogic 管理控制檯得到。也可以通過 Java profile 使用它們。
- 計時 (a.k.a.“實” 時):每事務,每組件,每方法;可以按統計平均值或單獨的數據點查看。Java profiler 可以產生一些這樣的數據,但是使用程序監控解決方案可能是您的最佳選擇。
- 內部資源:分配的數量,使用的數量,等待的客戶數量,獲得資源的平均等待時間,使用資源平均消耗的時間,使用資源完成請求工作時使用的平均時間。應用程序服務器通常會給出這些數字的最小可視性。
- 外部資源:分配的數量,使用的數量,等待的客戶數量,平均等待時間,加上對這些外部系統的直接測量(例如查看它能多快完成請求的工作)。不要忘記運行應用程序服務器的操作系統和硬件也是“外部資源”-例如,是否您使用了太多的進程或端口?可以用兩種形式測試這些資源―從 JVM 內部測試提供資源的橋接層,用外部資源本身的工具測量外部資源。
- 網絡應用:帶寬使用,延遲。雖然操作系統自帶的工具(例如 netstat)也有助於做這些工作,但是網絡嗅探器設備可以深入瞭解這些信息。
- 系統狀態:用線程清除,日誌和跟蹤文件,堆棧跟蹤等等。或者在更深層次上,就像調試器中查看的那樣使用變量的值。
實驗工作
有的時候,在一次標杆運行中獲得的數據,不足以揭示答案。那麼找到答案的機會就在於您還有些有限的預算,可以運行試驗或者做實驗工作,來完成診斷。您可以運行什麼類型的試驗呢?您可以修改、觀察什麼變量呢?
- 嘗試觀察系統行爲在一段時間上的變化。應用一個衡定的負載,並觀察您的指標隨時間發生的變化。您可能會看到某些趨勢,短則一小時就可看到,長則二三天。例如,您可以看到內存使用隨着時間而增長。隨着使用的內存數量達到上限, JVM 花在搜索垃圾上的時間和操作系統花在分配內存頁面上的時間開始減少用戶事務的整體響應時間。當拋開 GC 的時候,垃圾蒐集的整個過程可能過長,從而造成執行中的事務超時和異常。現在可以開始查找資源泄漏或內存泄漏了。
- 嘗 試在系統上改變負載。 採用三到四種工作負載(例如,10個,50個和100個用戶),並蒐集每個負載的數據。分析一下您對這些負載的測量情況。例如,您可能發現,當您的賬戶登 錄 servlet 的響應時間無論如何都會低於 50 毫秒的時候,計算銷售稅的 EJB 卻隨着用戶數據的增長,速度呈線性下降。如果這個 EJB 性能在負載下的差異能解釋整體響應時間性能在負載下的增長,那麼現在就是深入分析這個組件的時候了。謹記一定還要把負載恢復原狀,並查看系統是否復原。
- 嘗試把系統分成小單元,針對每個單元輪流進行壓力測試。 選擇一個或多個座標系對系統進行劃分。主要的一個是系統面上的層:負載均衡器、Web 服務器、WebLogic Server,以及後端。其它示例包括用戶賬戶,內部組件,事務類型,以及單獨的頁面。假設您選擇了用戶賬戶。在用戶 A 的賬戶下運行一些負載,然後再在用戶 B 的賬戶(應當非常不同)下運行某些負載;比較兩次運行間不同的測量結果。或者,輪流選擇您使用的後端系統,分別對使用每個後端系統比較重的應用程序組件進行壓力測試。具體要選擇哪個座標進行劃分,完全取決於您要證明或者否定哪個假設。下面是一些具體想法:
- 如果某個用戶的登錄看起來造成了問題,那麼有可能是這個用戶賬戶檔案(例如,裝入 2,000 個訂單的完整採購歷史),或者可能是他使用系統的方式(例如,頁面訪問的順序,或者他用來查找某個文檔的查詢字符串的正確性)。
- 如果您使用的是一個集羣系統,請嘗試以單臺機器爲單位劃分。儘管盡了最大努力,有的時候還會有一些機器沒有安裝最新的應用服務器或者操作系統補丁,這就會 造成不同的性能特徵。而且,還請注意負載均衡器,或者保姆進程,查看它們是否公平地分配了工作並跟蹤了進入請求。
診斷:測試您的推測
在這個時候,您應當已經有了足夠的信息,可以形成關於性能瓶頸原因的推測了(請參閱表 1)。爲了驗證您的推測是否正確或者在多個備選的推測之間進行篩選,您需要分析更多的信息或者在系統上運行更多的標杆測試。這裏是一些可以幫助您的指導意見:
- 區分糟糕的編碼(或者是應用程序組件或者是橋接層)和瓶頸 (內部的或外部的),請查看整體的 CPU 使用情況。如果它在負載下沒變化,但是整體響應時間變化了,那麼就是應用程序花費了它的大多數時間來等待。
- 僅僅是因爲好像是外部資源的問題,不代表您就可以立刻把責任推到資源上。 例如,分層或聯網的問題,也能造成數據庫看起來很慢,雖然實際上它並不慢。或者,更簡單一點,您對數據庫的請求可能是不合理的(每次用戶登錄的時候,都要進行三個表之間的 200 萬行記錄合併)。應當一直把橋接層的響應時間(例如,JDBC 驅動器)和資源提供的時間或者工具提供的時間進行比較(例如,DBA Studio)。
- 結構化的圖表有助於您理解系統內部的整體交互,但是不要忘記地圖並不是領地。.編碼錯誤或者對架構意圖的誤解,有可能使系統的實際行爲與期望的行爲不同。請相信性能工作提供的實際數據,而不要相信一個聲稱“每個用戶事務將只會發佈一個 SQL 語句”這樣的文檔。
- 使用 Occam 軍刀。 假設您有兩個備選推測,一個是:在 200 萬行代碼裏,有一個編碼糟糕的組件,直到上週這個組件才集成進來;另一個是 JVM 的即時編譯器生成了糟糕的機器碼,破壞了這個變量的內存完整性。除非您有具體的數據來證實,否則(我已經看到了第二種情況發生),請更詳細地檢查第一個假設。J2EE 系統確實容易出錯,但是不要讓這一點就妨礙您先測試一個更簡單的假設。
- 日誌文件中沒有錯誤不代表不存在錯誤。 有許多原因可以造成不在日誌裏寫下異常;可能是因爲程序員認爲某件事“永遠不會發生”,於是就排除了這個異常;也可能是因爲某些組件可以使用故障恢復機制,所以就沒有記錄第一次故障。
示例診斷
讓我們來實際研究一個示例。您的 WebLogic 應用程序表現出在負載下越來越慢的症狀。您加入的用戶越多,系統越慢。一旦消除負載,系統就冷靜下來,沒有任何後遺症。您對這一主要症狀進行測試,發現並得到圖 2 所示的以下結果(時間測量針對的是單一典型事務的端到端完成時間)。
表2
負載(用戶數) | 來回用時(毫秒) |
10 | 300 |
50 | 471 |
100 | 892 |
150 | 1067 |
應用程序性能在負載下越來越慢
您形成了幾個假設。也許這裏的毛病是一個糟糕編碼的組件,也許是後端系統的瓶頸。它可能是一個同步阻塞點。您怎樣才能分清它們的不同呢?假設您還測試了應用程序服務器在負載運行期間的CPU整體使用情況,並得到了表 3 所示的結果。
表3
負載(用戶數) | 來回用時(毫秒) | 整體 CPU 時間(%) |
10 | 300 | 30 |
50 | 471 | 33 |
100 | 892 | 37 |
150 | 1067 | 41 |
問題看起來好像是“等待”瓶頸
現在看起來,系統不是 CPU 密集型的,這就是說它的多數時間都花在等待上了。那麼是它的內部(例如,同步交通阻塞)或外部(緩慢的數據庫)的問題?好,讓我們假設我們還稍微多蒐集了一些數據,如表 4 所示。
表4
負載(用戶數) | 等待數據庫連接的線程數量 | JDBC 查詢用時(毫秒) |
10 | 2 | 58 |
50 | 3 | 115 |
100 | 3 | 489 |
150 | 4 | 612 |
問題是否出在一個緩慢的 SQL 語句上呢?
現在看起來並不是內部等待數據庫連接的瓶頸,相反,好像是 JDBC 查詢本身的問題。JDBC 查詢不僅隨整體事務時間的不同而不同,而且它糟糕的性能還解釋了整體性能糟糕的原因。但是,我們還不能完全確定。您仍然還有三個主要的假設要排序:是否數 據庫本身慢?應用程序是否對數據庫進行了不合理的請求?或者問題是不是出在應用程序和數據庫之間的某個層上?您拿出數據庫供應商專用的工具,從它的角度查 看響應時間。假設您看到如表 5 所示的數字。
表5
負載(用戶數) JDBC | 查詢計時(毫秒) | DB SQL 執行的時間(毫秒) |
10 | 58 | 43 |
50 | 115 | 97 |
100 | 489 | 418 |
150 | 612 | 589 |
實際是數據庫中的 SQL 語句慢
如果您沒有看到這條信息,那麼您可能要返回 JDBC 驅動程序,期待能夠發現其中的某些同步問題(請記住,CPU 沒有被抑制)。幸運的是,在這個案例裏,您已經把具體問題的範圍縮小到數據庫查詢上。找出查詢請求是否足夠合理,需要一些相關領域的知識,需要熟悉系統, 但是在這個案例裏,它也許就是發現查詢把非索引字段和外鍵進行了比較。您和 DBA 協作,它修改索引方案,讓查詢變得更快,而您則找到了您的藥方。
結束語
診斷 WebLogic J2EE 應用程序的性能瓶頸是一個艱苦的旅程。請隨時保持清醒,把事實與推測分開,總是用實際的證據來確認您的推測。我希望給您帶來了思考和實踐問題的一些有用的想法的分類。就像調試,這仍然是一項不明確的藝術,但是深思熟慮會帶您走出困境。祝您好運!
作者簡介 | |
John Bley是Wily Technology 的軟件工程師。他有豐富的 Java 編程和架構經驗。爲了撰寫本文,他總結了 Wily 的企業客戶的經驗,Wily負責管理複雜的 J2EE 環境。 |