任何一個嚴肅的軟件或者網站都應該有明確的性能需求,這個需求在軟件設計之初就需要考慮,因而成為了影響軟件或者網站架構的一個重要因素。從這個角度來看,性能/可擴展性這樣的非功能需求其實比很多功能需求更加重要。為了明確滿足性能需求,性能測試自然是必不可少的一環,把這個環節做成熟,是我最近一直在學習考慮的事情。
然而這件事情不簡單,Herry H. Liu 在他的 Software Performance and Scalability: A Quantitative Approach 一書中寫了這么一段話:
It is generally agreed in the software community that it takes at least five years for a software engineer to become proficient in testing, optimizing, and tuning the performance and scalability of a software system.
且不說這件做好這樣的事情是否真需要積累五年那么長時間,就我最近的一些實踐來看,光測試一個簡單的 HTTP 服務并看懂并理解各項指標就不是件容易的事情。例如說,我們通常關心的一個網站性能指標是響應時間(Response Time),即我發出一個請求到我收到完整返回的時間,以下是我一個測試的結果報告:
當并發的請求數到達某個數值的時候(圖中大概為160),響應時間直接上升并產生了相當比例的失敗返回。這大概表明了系統的壓力極限,那好,下一步要分析的是,這個壓力極限背后的瓶頸是什么?哪一塊資源不夠用了?這就好比,當一個人跑步跑了一個小時之后,跑不動了,原因是什么?缺水?缺糖?腿部肌肉鍛煉不夠?心肺功能跟不上?腳拐到了?原因多種多樣……
對于一個軟件系統來說,需要分析的地方大概有:CPU、內存、磁盤I/O、網絡I/O、外部系統依賴;如果是Java應用內部的性能問題,還要分析JVM的GC、線程數等等;此外,還有數據庫性能,或者其他我想不到的…… 可以看到,這其中的每個點,都是一個很大的主題、都涉及很多理論、很多工具,當所有這些融合在一起工作,找出其中出問題的地方顯然不是件容易的事情。這和上醫院看病很像,一個表面看起來很簡單的感冒,搞大了醫生會驗血、驗尿、做B超甚至CT,道理是一樣的,無非就是收集全面的數據并基于此得出更準確的判斷。
提到性能測試就必須提一下系統的可擴展性,單獨測試單個用戶請求的響應時間意義是有限的,除了響應時間我們還要關心吞吐量(Throughput),即單位時間內系統能處理的請求數量,一個常見的吞吐量指標就是TPS(Transaction Per Second),每秒完成的交易數。需要注意的是TPS這樣的數字和并發數是不一樣的,一秒內可能完成了1000個交易,但同一時間的并行的交易可能只有200(前0.2秒200并行,完成后,再來200并行,以此類推)??傊覀兿M?,在響應時間位于可接收的范圍內這一前提下,系統的吞吐量盡可能大。至于可擴展性,通常指通過水平擴展,指的是能夠通過水平增加硬件(單臺機器2G內存變8G是垂直擴展,搞4臺2G機器是水平擴展)提高系統吞吐量。當然,如果不清楚系統的資源瓶頸,擴展也就無從談起。另外,如果系統架構天生不支持水平擴展(例如,把用戶狀態緩存到應用程序內存中,應用程序擴展到多臺機器后,這些狀態咋辦?),那就死翹翹了。
我相信,有基本素養的開發人員/測試人員,都會手動做一些性能測試,例如用簡單的 Apache ab 看下系統性能。然而,把性能測試正式地集成到開發流程中,不是件容易的事情。ThoughtWorks Radar 所提倡的 Performance testing as a first-class citizen 說的大概也就是這個事情。
如果能做到這一點,好處是顯而易見的,你幾乎每天都能很方便的得到系統性能報告,當代碼變更導致系統性能下降的時候,能快速得到反饋,能大大降低性能瓶頸排查的成本。當你對系統進行性能調優的時候,也能得到及時的反饋。然而,為了做到這一點,你至少需要:
1. 盡量模擬真實線上的環境
2. 全自動、快速地持續部署
4. 量化的指標以實現自動驗證
這幾條要求團隊擁有非常全面的技能,能把握性能需求,能設計可靠的測試用例、能分析量化的指標、能持續部署系統……你感受下,看看自己團隊是否有這個能力。
軟件性能和可擴展性是個非常有意思也非常有挑戰的主題,以上是我首次真正去學習實踐的小小總結,我會進一步學習思考,記錄心得。