Web App性能優化之亮劍

BetWhicker 8年前發布 | 4K 次閱讀 性能優化

自計算機誕生以來,系統性能問題亙古未變,從指令級優化到集成系統的優化,可謂愈來愈復雜。每種類型的性能問題即便出現的場景不盡相同,但依然有一些性能優化模式,久經沙場考驗,不斷被積累下來。性能問題本質上是一個可觀的問題,對于Web App我們更多地可能是談論與“唯心”相關的問題,最簡單的司空見慣的對性能的描述就是,“這系統慢的要死”。接下來,我將以我的經歷,談談如何對Web App的性能優化亮劍。

性能指標:

既然,系統需要優化,那么我們必須有一種方法能夠量化性能。響應性、響應時間、網絡延遲、單位時間內處理的請求數或者是Transaction數量,這幾個維度既幫助我們從用戶心理角度出發理解性能參數,又可以幫助我們量化性能改進的程度。

基于B/S架構的Web App性能問題,按照前后臺任務的不同,一般可以歸結為以下幾類:前臺數據渲染性能問題、后臺數據處理時間,包括讀取和存入以及Report報表。下面分別來談一談。

1.前臺數據渲染問題:

對于企業級應用系統,尤其是大型企業的應用系統,其數據規模相當可觀。至于并發量,其相對于電商類的O2O系統來說,可能顯得有點微不足道,但依然存在其本身固有的復雜性。當然,對于渲染海量數據我們有很多種選擇,這主要受系統架構的支配和影響。

比如如果采用分頁顯示,每頁只需要顯示少量數據即可,用戶可通過翻頁尋找期望的數據。然而,對于采用OnePage架構風格的系統而言,渲染大量數據就顯得沒那么簡單了。如果終端用戶的機器配置不是很高端,那更是雪上加霜。但更加令問題棘手的因素是,對于海量的數據,不僅僅需要展示,而且需要頁面邏輯,比如批量更新、頁面排序、用戶交互等。針對這一性能問題,業界也嘗試給出了它們的答案,比如ReactiveJs的出現,使得前臺渲染海量數據變得相對簡單和成熟了許多。

對于AngularJS,雙向綁定、自定Directive、靈活多變的頁面處理方式令其一出生就光彩奪目,但是其由于臟檢查而又令人深惡痛絕,在渲染海量數據時更是暴露無遺。有一份來自項目上的數據,在單核CPU,、1G內存的機器上,渲染500條、每條平均只有50個字符的數據記錄,只要稍微滑動一下鼠標,內存以及CPU的使用率將突增至100%左右。

對于AngularJs的這種詬病,ReactiveJs在誕生之初就考慮到了渲染海量數據的問題。AngularJs雖然本身并沒有很好地解決這些問題,但是AugularJS提供給了用戶非常靈活的方式,以構建高性能的、基于用戶友好的大量數據渲染的解決方案。

前臺海量數據渲染問題,歸根結底總是只能顯示一屏幕數據,即便用戶非常貪婪,主觀上期望顯示出所有滿足條件的數據記錄,但受制于屏幕大小,永遠只能看到一屏幕數據。 其實,用戶的本質需求是,隨著用戶輸入條件的變化或者是平滑滾動,用戶看到的數據會隨著用戶行為朝著預期的方向進行加載,只是這樣而已。 因而無論ReactiveJs還是AngularJs,或者其他的JS框架,只要處理好了這個需求,就可以很好地處理前臺渲染性能問題。

2.后臺數據處理問題:

基于MicroService或者Web Service構建的Web App,以及采用RDMS作為存儲介質的系統,性能瓶頸可能出現在多個方面,比如網絡資源、服務器內存、I/O讀寫以及多線程CPU資源,這些都與系統本身采用的架構息息相關。

網絡資源:

網絡資源或者是網絡帶寬性能問題,在這里可能更多地是談論它的使用效率,最常見的問題包括重復的網絡請求、請求處理的跳數多、響應數據量大而臃腫等。

服務器內存:

將大量無關數據讀入內存做處理,是服務器內存性能問題的主要表現之一。比如搜索功能,所有的與搜索有關的數據和邏輯應該有專門的搜索引擎,而不是簡單地將數據讀入業務應用層中的內存中實現搜索功能。樹型搜索或者Hash比鏈式搜索,在速度方面占據很大優勢。

I/O瓶頸:

I/O最常見的性能問題可能就是被大家熟知的N+1問題,它只存在于關系型數據作為存儲介質的應用系統中,并且是只有在采用ORM框架的情況下才會出現的特有問題

3.報表性能問題

數據挖掘或者BI都需要對原始的龐大數據進行特殊處理,數據庫模型或者是數據處理的方式是影響報表性能問題的關鍵因素。報表只是對數據進行讀取操作,不涉及到數據更新,這一屬性導致報表的數據模型理應與進行業務處理的讀寫數據模型是不同的。在原始的關系型數據庫中,一旦報表的數據模型與業務數據模型一致,就容易出現表級別的數據庫連接操作,這無疑會影響整個報表的性能。

性能優化的策略與手段

針對單純的前臺數據渲染問題,處理起來相對來說比較簡單,一個方案就是靜態構建一屏幕數據的DOM結構,動態加載用戶數據,無需構建與數據記錄等量的DOM元素。后臺數據處理的性能問題,處理起來就相對比較棘手,首先受到既定的技術架構的限制,其次在既定的技術架構下,如何合理的劃分邊界,包括領域模型與API邊界,本身就是一個見仁見智很難做出選擇的復雜問題。

對于重復的數據請求,處理起來比較簡單,只需要去掉冗余的請求即可。如果把對于用戶請求的處理邏輯看做服務的話,那么如何設計服務以及定義服務邊界就成為了影響性能的關鍵因素。這背后體現的是用戶需求的本質,以及對時間的分片處理。這就要求服務之間關注點各自分離、邊界比較清晰,能夠適應橫向擴展。比如采用Publish-Subscribe模式的系統是一個很好的選擇,但是模式永遠只是指引的方向,如何很好地應用這些模式,同樣需要對業務系統進行不斷的探索。一旦設計了這樣的服務,不僅能夠靈活支撐擴展性,也使得持續的性能優化成為可能,不僅如此,這樣的服務設計,還能夠幫我們靈活調整優化策略,比如采用多線程、異步、服務自治、分布式或者集中式節點擴展、靈活選擇數據持久化機制等等

處理N+1問題,更多地與業務模型以及業務上下文有關,從DDD的角度考慮這個問題,N+1問題好像不應該存在,這也說明了lazy-load對于DDD而言可能是一個壞味道。但是,解決N+1問題本身也相對比較容易,只要對相應的ORM框架有足夠深入的理解以及借用SQL Tuning工具,解決大部分的性能問題。

針對報表的性能問題,僅僅依靠集中式的高性能的單數據庫服務器,通過操作表進行數據讀取,或者進行連接操作,或者進行映射操作,并不能滿足用戶對于性能的需求。尤其當有報表中存在業務邏輯時,比如用戶權限控制,將使得出報表本身也變的非常復雜。因而,NoSQL以及分片和數據復制可能在這方面有更為出色的表現。

性能優化展望

性能問題是一個復雜的領域問題,解決性能問題關鍵是找出性能瓶頸,但是如果永遠只能“東窗事發”之后進行補救還遠遠不夠,因而在解決系統性能的道路上,需要在系統開發時就給予足夠的重視,甚至在架構決策時,也應該考慮性能的需求。在今天分布式處理以及大數據技術飛速發展的大背景下,對于性能的解決,我們也許還有更多的選擇。

 

來自:http://insights.thoughtworkers.org/web-apps-performance-optimization/

 

 本文由用戶 BetWhicker 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!