應用效能問題解決實際案例
某專案在上線前的APT(Application Performance Testing應用程式效能測試)過程中發現效能問題,效能測試結果影響是否上線,緊急求助外部專案組技術專家。
因分屬不同專案,只能通過專案組提供的資訊進行分析。
第一輪評審
現象
根據APT效能監控截圖,左圖為併發使用者數和CPU使用率,右圖為單獨的CPU使用率情況。APT測試情景為併發使用者數以5分鐘為週期進行增加,增加使用者數25人,在40分鐘後達到最終200併發使用者並保持1小時200使用者活動狀態。
很明顯看出使用者穩定後CPU使用率還在持續增長,懷疑有記憶體洩露問題。
分析
第一輪我們拿到的是API原始碼,日誌裡面出現outOfMemory錯誤,檢查一遍發現API控制器方法中建立了HttpClient例項。詢問開發組回覆是因為每次請求URL不同,根據經驗推薦採用HttpWebRequest替換調HttpClient。同時我們調查是否是由HttpClient引起的記憶體洩漏問題。
由Google搜尋結果HttpClient確實有記憶體洩漏的報告,我們著手寫了一個小程式比較HttpClient和HttpWebRequest效能,並通過JMeter在本地進行效能測試。
HttpClient和HttpWebRequest效能比較
測試環境
測試URL: https://www.baidu.com/#{number} (number變數為隨機或遞增以防止HTTP快取干擾效能對比結果)
測試節奏:10秒週期,併發使用者從1到200增長
測試結果(記憶體使用量)
- HttpClient: 455MB
- HttpWebRequest: 242MB
圖1: HttpClient (GetAsync)
圖2: HttpWebRequest (請忽略結尾處由網路連線問題出現的凸起)
結論
將HttpClient更換使用HttpWebRequest,但根據HttpClient記憶體使用陡增幅度估計還有其它問題,通知開發組繼續調查並提供相關資料。
第二輪評審
現象
比較IIS記憶體使用和SQL Server的記憶體使用發現相同的增長趨勢,猜測瓶頸出現在資料庫查詢中(如果是拒絕訪問資料庫不會出現記憶體增長,一定是已經連線了資料庫併產生查詢問題導致)。
推薦增加查詢操作日誌獲得查詢執行時間,安裝資料庫效能監視工具(可使用Windows Server自帶的Performance Monitor或者第三方工具如AppDynamic),檢視查詢的執行計劃(Execution Plan)。
圖3: SQL Server – 記憶體 - % committed Bytes (記憶體使用)
圖4: MS IIS – 記憶體 - % Committed Bytes (記憶體使用)
同時經過了解API還會呼叫上游系統的API(API巢狀),所以考慮檢查日誌是否存在因上游API瓶頸導致呼叫失敗,導致異常阻塞請求程序。也會出現相同的現象,開發組著手調查。
上游API問題通過調整系統配置消除瓶頸,但SQL Server效能問題還無任何頭緒。只能安排時間一起評審資料庫及相應查詢效能問題。
第三輪評審
背景:
測試總時長1小時40分:從最開始每隔1分鐘增長5個併發使用者,40分鐘左右併發數加到200,然後維持併發使用者不變,又執行1小時
現象:
達到200併發後的20分左右時, CPU的使用率達到30% 左右之後保持相對平穩的比例,但是在這之後的執行過程中 request queued 曲線出現排隊現象, Thread Count 曲線出現突然增長的情況,並且同是response time 翻倍增長。
上次報告沒有HTTP 5xx錯誤,本次出現HTTP 5xx錯誤,27日錯誤數量激增。
程式中有從上百萬條資料中抽取資料生成級聯選擇(下拉選單)的UI元素。
程式中使用了Http Cache,但在準備Cache資料的邏輯中沒有鎖處理,資料準備時間過長,就會造成期間大量請求訪問資料庫。
效能測試報告中SQL Server也顯示出週期性達到100% CPU利用率,引發資料庫連線超時,同時應用程式出現GATEWAY_TIMEOUT。
System.Data.Entity.Core.EntityException:
The underlying provider failed on Open. --->
System.InvalidOperationException: Timeout expired. The timeout period
elapsed prior to obtaining a connection from the pool. This may have
occurred because all pooled connections were in use and max pool size was
reached.
分析
因已經做過兩輪評審,開發組這次提供了全面的資料,通過程式碼評審發現邏輯中有使用LDAP(Windows活動目錄輕量目錄訪問協議Light Directory Access Protocol)遍歷登陸者所有活動目錄下的組。
此邏輯可能會產生請求ADFS查詢的瓶頸,阻塞請求。
同時發現部分被查詢的表中沒有建立索引(這個是在最初問過開發團隊但得到的回答是肯定的)並進行索引優化,另外,API採用.NET Entity Framework編寫,由SQL查詢監控看出因無索引導致EF生成相同表的巢狀(笛卡爾積)查詢,是導致表遍歷和產生表鎖的主要原因。
對於大量出現的5xx錯誤,因上次效能測試沒有,主要針對修改的程式碼進行評審發現增加了EF查詢邏輯,這也是導致迴圈巢狀的原因。
結論
表索引優化是主要解決辦法。EF查詢最終改為明文查詢並SQL查詢優化。
總結
效能問題首先由測試入手,根據測試報告發現錯誤和效能瓶頸,主要以解決錯誤消除瓶頸為主要原則。對於有資料庫存在的情景,注意查詢和索引優化,保證連線池有效支援應用併發。問題調研期間要了解整體架構(本次API巢狀問題和LDAP的使用都是後期由開發組告知)和所使用的技術,才能給出全面建議。從開發組角度應該訓練發現效能問題的敏感度。所有問題的發現和解決以測試事實資料說話,不會存在莫須有的效能問題,所有問題皆有因