ASP.NET性能優化的15個技巧
譯文【51CTO.com快譯】引言:2018年,加載速度和用戶滿意度仍會是各個Web應用所要面對的首要問題。緩慢的加載時間和不佳的互動體驗都可能將客戶推往他處。本文向你提供15個ASP.NET性能優化的小技巧。
對于ASP.NET的Web應用來說性能一直是非常重要的。各種證據表明,緩慢的加載時間和不佳的互動體驗都可能將客戶推往他處。即使是那些對于用戶來說別無選擇的內部應用,他們的滿意度也是會與速度掛鉤的。
我們可以用很多種方法來提高網站的性能。在此,讓我們來看看其中的十五個。
1.量化一切
首先要做的***件事是收集應用程序的性能基線。有時候,你的本意是想通過一些修改來提高網站的性能,但是實際上性能卻被降低了。雖然談不上是個黑盒子,但是對于性能的調優,確實會時常給我們帶來意想不到的效果。因此,量化性能需要有一個全面的對于服務器、JavaScript和加載效率的衡量。除了手中的秒表,你還需要有諸如Prefix之類的各種性能測試工具(請參見:https://stackify.com/prefix/)。
Prefix能讓你更關注于低效的查詢,以及大量的JavaScript文件等方面。量化的數據會告訴你,對于哪些方面的優化能夠真正起到作用。你可以自己生產一張列表,并按照優先級進行排序。而那些位于列表底部的,則往往是一些重要程度最小、你幾乎可以忽略的方面。
2.先摘下“低垂的水果”
通常情況下,你應該優先處理上述清單中影響比較大的部分。很明顯,如果你能盡快地向用戶證明性能影響的“痛點”所在,你就能從精神上和資金上獲得更多的優化支持,當然這也是你不斷進行性能調優的動力。比如說:全局性的因素(如JavaScript、CSS、及其同類的加載)都會比單個頁面的修改產生更大的影響。
本文后面介紹的內容,將粗略地以影響程度從大到小來依次展開。當然,根據各個網站的差異性,你可以有所取舍地根據自身情況來予以實踐。
3.啟用壓縮
HTTP協議并非是一個特別有效率的協議。在默認情況下,它不會對內容進行壓縮。雖然有些網絡資源,比如說圖片已經被壓縮了,但是像HTML、CSS和JavaScript通常還是以文本的形式在進行傳輸。就算是最古老的瀏覽器也能支持通過gzip的算法來對HTTP內容進行壓縮,而且gzip大約能夠壓縮HTML文件的三分之二。也就是說:一個100KB的文件被壓縮后最終至多只有33KB,這比例是相當驚人的!
與之對應的另一個改良算法是Brotli(請參見:https://en.wikipedia.org/wiki/Brotli)。其效果更佳,而且能夠被當前大多數瀏覽器所支持。
4.減少HTTP請求
實際上,瀏覽器每打開和建立一個與服務器的連接,都是要“交稅”的。而這個稅收就是以TCP/IP連接的開銷形式來體現的。這個現象在那些具有高延遲、并需要花費很長時間才能建立新連接的情況下,就顯得尤為突然?;谶@個客觀事實,為了減少HTTP的請求數量,瀏覽器應該將它們的請求限制在單臺服務器上,以實現***程度的優化。
延遲與帶寬
我們在優化網頁的加載時,理解延遲和帶寬之間的差別是非常必要的。讓我們想象一下:你有20頭驢,你需要從坐落在班芙的一個集散點轉移到另一個坐落在大峽谷的集散點。為了讓這些驢子能夠盡快地轉移過去,你需要優化兩個方面:你一次性能夠轉移的驢子的數量,和單次轉移驢子所需要的時間。
帶寬就像是你一次性能夠轉移的驢子的數量。在高帶寬的情況下,你可以一次性將多個驢子放入家畜車進行轉移。而在低帶寬的情況下,你只能將一頭驢子放在你那2001年產的本田思域的單個座位上進行轉移。
而延遲則是你從班芙到大峽谷,進行單次轉移驢子所需要的時間。高延遲意味著沿途有多次延誤,所以減慢了單次的轉移時間。而低延遲則意味著你通宵達旦地一路狂奔,不會在任何旅游景點處停車觀景。因此在理想情況下,你應該一次性轉移盡可能多的驢子,而且避免沿途上的任何停頓。
封裝
根據向服務器請求的資源類型不同,我們可以用幾個不同的方法來降低請求的數量。對于JavaScript,可用類似webpack、gulp或grunt的工具將各個JavaScript腳本捆綁“串聯”到一起成為一個單獨文件。我們也可以使用相同工具里的不同任務,來將CSS文件合并為單一文件。
而對于圖像則有點復雜。如果你在網站上使用了許多小的圖像,那么就可以使用CSS Spriting的技術。比如說:我們將所有的圖像合并成單獨的一個,然后使用CSS的偏移量將圖像進行轉換,只顯示我們所需要的單個子圖像。還有其他的一些工具,能夠簡化該過程,不過它們都需要人工的干預。而另一種可選的方法則是使用icon font。
5. 基于SSL的HTTP/2
HTTP/2是HTTP的新版本,它進行了許多非常有用的優化。首先,在上面提到壓縮方面,它進行了擴展,同時也包含了各種協議的包頭。更有趣的是,在與服務器之間的連接上,它使用一種被稱為“管道”的機制,來傳輸多個文件。這就意味著:通過合并文件來減少HTTP請求的方式在很大程度上已經不必要了,而它的效率則更高。
如今,幾乎每一種瀏覽器都能支持HTTP/2。但頗具諷刺意味的是:在服務器端的支持上卻有著各種限制。例如:在寫數據的時候,Azure的Web應用程序就不能支持HTTP/2。
不過,服務器現在也能夠對網頁上的內容做出智能的決策,并在它們被請求之前就主動向下推送資源。因此,如果索引(主)頁面上包含一個JavaScript文件的話,它只有在瀏覽器解析了整個頁面之后才會被發現;而現在的服務器完全可以根據指令,在瀏覽器意識到它之前,就主動將該文件傳輸過去。
因為所有支持HTTP2的瀏覽器都能夠提供HTTPS的服務,因此SSL自然也是必需支持的技術之一。
6.縮減文件尺寸
雖然壓縮對于減少傳輸線路上的數據總量來說是一種很好的手段,但是所有的壓縮算法對于發送HTML、CSS和JavaScript來說都是無損的壓縮方式。這就意味著對compress(x)的結果進行解壓縮--decompress(x)總能得到x。在理解了這一點的基礎上,我們就可以尋求在尺寸上的進一步縮減了。舉例而言,如下的JavaScript:
- function doSomething(){
- var size_of_something_to_do = 55;
- for (var counter_of_stuff = 0;
- counter_of_stuff < size_of_something_to_do;
- counter_of_stuff++) {
- size_of_something_to_do--;
- }
- }
它在功能上等同于:
- function doSomething(){var a=55;for(var b=0;b<a;b++){a--;}}
由于變量的作用域是完全私有的,因此空白的占位符就顯得沒有必要了。這個過程被稱為縮減。與壓縮技術類似,該技術也可以被應用到CSS、甚至是HTML上。
7.優先加載CSS
請務必優先加載你網站上的CSS內容,而且***放在頁面的頭部。
要理解這個原因,你需要先了解瀏覽器是如何實現其驚人的瀏覽速度的。在下載頁面的同時,瀏覽器會盡快將其從應用程序那里得到的內容渲染出來。通常情況下,由于瀏覽器并不知道頁面上的哪些內容是無效的,因此它只能像玩猜謎游戲那些做出猜測。
之后,當瀏覽器意識到其猜測有誤時,它不得不拋棄之前所做的一切,重新開始渲染。其中導致這些返工的一個原因就是可能要增加新的樣式表。因此,優先加載樣式表,就可以避免對已經渲染的元素進行返工。
8.***加載JavaScript
JavaScript則是CSS的另一面,應當***被加載。這是因為我們希望頁面能夠被盡可快地渲染出來,而JavaScript則沒必要在一開始就被渲染。用戶通常會花些時間來閱讀頁面的內容,以決定下一步做什么。那么這個時間窗口就被用來在后臺加載腳本、和顯示頁面上的交互效果了。
不過,這里需要注意的是:如果你的網站上有大量的JavaScript,例如Angular或React之類的應用,那么你可能會發現***加載JavaScript反而不一定是***的方案。你不妨考慮采用只加載JavaScript的必要部分到bootstrap的應用中,并且在后臺加載更多的JavaScript。如果速度對你來說非常重要的話,你甚至可以考慮所謂的同構或是通用的應用程序。因為在這些應用中,各個頁面在服務器端被渲染,然后JavaScript應用被附加到已渲染的HTML中,并予以接管。這些應用程序具有在加載的過程中無縫銜接的速度優勢。
9.縮小圖片
在理想狀態下,你的網站根本不會包含任何圖片。通常情況,矢量圖片會比各種真彩圖片要小許多,因此如果使用inline-SVG和CSS的技巧來為你的網頁創建矢量圖的話,效率會非常高。然而事實上卻不能完全是這樣,因此,你必需進行一些縮小圖片的工作。雖然要搞清楚各種正確的編碼設置著實不容易,但是我們可以借助像tinypng之類的服務(請參見https://tinypng.com/)來達到目標。另外,它的logo是一只非??蓯鄣男茇?。
當然,也有一些JavaScript構建工具的其他插件,可以達到類似的優化效果,請參見:https://www.npmjs.com/package/gulp-image-optimization。
10.檢查你的查詢
各種對象關系映射(object-relational mappers,ORM)已經對開發人員的生產力起到了提升作用,但它們只是提供了優化查詢的一個抽象層。當你可能會有N+1個選擇錯誤、或是從服務器獲取了太多的數據時,只有通過Prefix才能夠突顯具體的次數。你會驚奇地發現通過使用預先加載、而非延遲加載,以及檢查各種預測值,問題就會變得非常容易解決。當然,微軟對于優化實體框架(Entity Framework,EF)的各種SQL調用,也有一些獨到的建議,請參見:https://msdn.microsoft.com/en-us/library/hh949853(v=vs.113).aspx。
11.緩存你的頁面
通常情況下,你網頁上的數據會隨著時間的推移而緩慢發生變化。比如說:在Stack Overflow(譯者注:它是一個技術問答網站。用戶可以在該網站上免費創建主頁、提交問題、瀏覽問題、索引相關內容。)上的那些熱點問題的頁面會實時地被更新,但是其數據的變化并不夠顯著,不至于觸發對數據庫的重新查詢。因此我們沒有必要去調整數據庫、并對復雜的頁面進行重新渲染,而是可以將該網頁推送到緩存隊列中,并使用這些數據來響應后續的請求。
如果你碰巧使用著ASP.NET的MVC緩存,那么一個action的響應就應該只是簡單地添加單一的屬性到action中。
- [HandleError] public class HomeController : Controller {
- [OutputCache(Duration=10, VaryByParam="none")]
- public ActionResult Index() {
- return View();
- }
- }
如果你所緩存的頁面全部內容并非是你都需要的,那么請繼續往下看第12個技巧。
12.僅緩存頁面的部分內容
你可能只想緩存頁面的一部分內容; 這被俗稱為甜甜圈孔洞緩存(donut hole caching)。當你在同一頁上,既有用戶特有的數據、又有一般的數據時,這會是一種非常有用的方法。用戶特有數據隨著用戶的不同而改變,而頁面上的其余部分則對于所有用戶都是相同的。在MVC 5的應用中,這是通過做局部視圖(請參見:https://visualstudiomagazine.com/articles/2017/05/01/doughnut-hole-caching.aspx)來實現的,并且在MVC的核心,我們會用到緩存標簽幫手(請參見:https://www.davepaquette.com/archive/2015/06/03/mvc-6-cache-tag-helper.aspx)。
13.內容分發網絡(CDN)
全球各處都有非常多的內容交付網絡,它們可以通過離你最近的節點,將內容高速地傳遞到你的面前。
14.縮小你的庫
如果你正在使用像jQuery之類的庫,那么你可以考慮是否真的會用到它的所有功能,也許你可以去使用一個更小、更有針對性的庫。比如說:Zeptojs就是一個能夠支持jQuery的很多功能、而又更小的庫。其他像jQuery UI的庫,也能為構建個性化的包提供刪減過的功能。如果你正在使用Augular的話,那么你在對產品進行編譯的時候,就可以像“搖動樹干”一樣,將那些在你的項目中完全用不到的庫去除掉??梢娺@種方式在保留同等功能的情況下,能有效減少傳輸中的數據載荷。
15.避免客戶端重定向
***的技巧是避免通過使用客戶端的重定向。重定向勢必添加了額外的服務器跳轉的開銷。而在諸如蜂窩網絡這樣高延遲的網絡中則是完全不可取的。相反地,如果使用服務器端的重定向,則不會增加跳轉的開銷。不過,此法對于將用戶重定向到SSL版本的頁面上卻不太有效果。針對這種情況,HTTP嚴格傳輸安全(HTTP Strict Transport Security,HSTS,請參見:https://www.troyhunt.com/understanding-http-strict-transport/)和預加載正好能夠提供幫助。只要你將網站加入預加載列表中,就會被自動重定向到相應的SSL版網頁上。
我們希望上述這些技巧能夠有助于提高你的網站性能,以及用戶的滿意度。如果你覺得我們疏漏了任何技巧的話,請留言為我們添磚加瓦。當然,你也可以擴展性地閱讀一下Matt Watson的《有關服務器和客戶端性能的三大Web優化技巧》(https://stackify.com/web-performance-optimization/)。
原文標題:15 Simple ASP.NET Performance Tuning Tips,作者: Simon Timms
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】