網站性能優化摘要

       在滿足相同用戶業務、功能需求外,很多用戶或許會選擇“最好”的服務提供商,可見在一個提供互聯服務的web網站,用戶體驗、網站性能是多麼的重要。本文結合自身的性能優化實踐經歷,做下簡要的總結,當然性能優化的範圍很廣,本文未能提供過於明細的介紹某些優化如何實現以及具體的優化方案,僅做簡要介紹,提供簡要思路供各位參考;另外當然性能優化在不同類型的網站、應用中關鍵點有所不同,以下只針對WEB應用型站點做簡要描述。

 

§、性能相關

       性能的範疇可以放大到所有的計算機軟硬件、虛擬資源中,即所有的硬件基礎設施、基礎網絡以及具有一定可執行能力的軟件都有可能存在着性能的因素,因此考慮到一個應用、一套服務的性能問題時也是如此。從Web應用性能優化的角度來說,一般會考慮到以下幾個方面:

一、硬件

       毋庸置疑,硬件爲應用支撐,作爲業務實施的基礎,應儘可能提升其吞吐、Process效率與承載,當然也要視各業務自身實際情況,來決定是否進行必要的提升。

二、網絡

       網絡環境影響各應用層的通信與穩定,影響請求響應與數據交換的效率與質量。一般來說,需要考慮選擇穩定、通信良好的機房環境。

三、架構

       從高層架構的角度,性能優化一般會考慮到:負載均衡、統一Cache、CDN、雙通網絡等等,都是可以考慮作爲改善性能的方案。對其中每一種方案細分,則可以有很多分支&實現方式;舉個簡單的例子,在做數據庫層面的負載均衡時,我們可將所有產品作爲一體,統一完成數據處理的負載與同步機制,另外也可以將產品進行細分,按產品類型來做負載,即每一類或每幾類產品作爲一個負載組……,當然結合業務、應用的實際情況作出取捨。

四、軟件

       拋開前面所說的幾大類相關因素外,餘下大部分工作則是應用層面的tuning,大致可分以下幾個方面:

1、客戶端

資源加載順序:HTML、DOM的Render,JS、Css、Image等等資源文件的加載順序,加載順序的不當可能會直接影響到頁面感觀,比如出現短暫空白、短暫停留以及不同引擎的瀏覽器出現不同程度的兼容性等問題。

資源下載量:在保證必需資源的情況下應儘量減少下載量,eg: 單個資源文件的下載量不可過大,應通過壓縮和緩存的方式減少下載。

連接請求數:請求連接數往往是Web請求的一個瓶頸,大家可能往往會看到,訪問一個站點出現連接數過多或系統過於繁忙的提示,鐵道部的站點就是一個很好的事例哈,當然不能說它就做的不好;究其原因,不同的瀏覽器在併發連接請求數上皆有不同的上限,且服務器端對請求連接數也有一定程度的上限承載。避免連接數過多的有效方法是適當合併資源文件,利用緩存機制。諸如:背景圖片合併、資源合併等

腳本執行效率:廢話不多說,腳本執行的效率同樣影響到頁面的展現與響應,這與腳本的寫法、實現密切相關。可以適當的結合異步處理方式可“改善”腳本的線性響應方式。

頁面佈局:頁面佈局與資源順序有些相關,涉及到如何組織HTML,決定哪些元素預先加載,決定頁面呈現的塊效果。HTML元素以及佈局的不合理也會增加解析器的解析時間。

其他:

(1)優化HTML元素、代碼腳本、實現與結構(大文件拆分)
(2)頁面腳本、樣式放置位置
(3)不用大的table、div容器(全部加載)
(4)啓用客戶端緩存
(5)參考YSlow、Firebug
(6)img script -> *.js

2、服務端

處理後臺事務,拋開架構層面的因素,性能瓶頸一般在於代碼的執行效率,以及事務處理的效率(儘可能提高併發和吞吐);關於代碼執行效率,也有很多基礎參考書籍,eg: Effective C#等,另外有很多技巧性的問題也起到了關鍵的作用,例如靜態變量實例的緩存、異步處理模型,循環處理改爲批處理等等,都是需要在實踐中多多的總結。

其他:

(1)靈活的應用緩存策略(遠程、分發、數據緩存、頁面緩存……)
(2)結合靜態數據、配置文件(不必要DB操作的儘可通過靜態數據文件實現)
3ViewState(不需要用的頁面、控件禁用掉)
4頁面(儘可能手動壓縮、自動壓縮頁面文件、元素大小,JS、CSS、圖片、HTML代碼等,另儘可採用DIV + CSS)
5數據分頁獲取(根據必要首頁可做緩存)
6少用服務期端的控件

3、數據庫

       關於數據庫優化的內容非常多,請參考相關資料。

       一般來說,主要考慮到SQL語句的執行效率、數據分區、索引等信息,以及負載、故障集羣。

 

§、應用案例:某充值、支付網站首頁

提供用戶充值、支付、轉賬、賬戶管理等多類型業務的綜合平臺,並提供對外充值、支付服務接口。

首頁特徵:服務產品展示與引導、公告信息、公共導航菜單等,另外含有動態內容,每用戶登錄後,顯示內容不同。性能上要求保證訪問速度在XXms內。

 

§、優化思路

1、 緩存

涉及到動態和靜態內容的結合,用戶的登錄、賬戶信息等因素,不便於對整個頁面做共享緩存、硬件緩存處理,所以頁面採用局部緩存來處理。當然如果所處理的事務不多,也可通過AJAX的方式來解決。另外可利用304未提交修改的機制,減少服務請求。

2、 資源延後加載

CSS、JS放置頁面尾部加載,也可考慮動態的方式來加載CSS、JS。

對於Iframe類資源,可以考慮延後設置src,原因不解釋。

3、 減少元素輸出

比如禁用ViewState、動態批處理元素屬性、事件,減少HTML元素輸出等。

4、 頁面壓縮

頁面壓縮可以在服務器端由IIS或網絡訪問控制層處理,也可以在應用站點中處理。

有關此信息網上有很多解決方案可供參考。通過應用站點的壓縮可以控制壓縮不同資源的靈活度較高,但是服務器端處理來說性能相對較低一些。

如果你無法應用這類壓縮方式,也可儘可能壓縮頁面大小(減少空格、減少元素輸出)。

5、 資源壓縮

資源壓縮一般考慮到JS、CSS,對JS的壓縮可以採用JSA(推薦)、YUI Compressor壓縮工具,對CSS的壓縮網絡上也有不少的工具。此類壓縮在應用部署上有一定的繁瑣性,但如部署時有輔助的解決方案,效果較佳。關於壓縮後增加了CPU的處理復旦,個人覺得可以忽略不計。

6、 請求連接數

(1)   考慮將JS、CSS資源進行合併,再進行壓縮,這樣大大的減少了資源數量,提高了壓縮的比率。

(2)   合併背景圖片、合併資源圖片

(3)   可適當考慮將公用部分或需異步處理的內容採用IFRAME的方式來嵌入。

7、 靜態化

靜態化、配置化站點都會使應用站點更靈活定製, 我說的靜態化不是類似後臺把頁面生成後輸出爲HTML,eg: 原本可以通過服務端交互展現產品列表&信息,但也可以通過靜態化爲客戶端數據後,異步來處理。如果做不到全頁面的靜態化,那也可以對頁面局部做靜態化處理。

8、 其他

(1)   通過增加不同類型靜態資源的靜態服務器可以優先考慮,方便統一策略的控制與應用。

(2)   很多時候可以考慮DIV+CSS的佈局方式,層次清晰且便於控制,

(3)   應避免類似找不到文件或者空src的情形

(4)   增加客戶端Tracker,便於記錄操作時長、錯誤日誌等行爲信息。

9、 測試、數據收集

對站點的測試,一般可以採用第三方產品,例如基調網絡(eg:

www.networkbench.com),做到在不同點數據準確的監測與採集(詳細的客戶端信息、頁面執行情況,出錯概率、用戶網絡等等), 便於進一步採取相應的優化措施。


 

§、總結

本文介紹的多是基本概念與基礎切入點,企業應用產品開發多數時候也是如此,正確的思想與切入點,纔有可行的實踐方案。本文可能描述的不是很全面(只能點到爲止),在實際項目中遠不止這些,具體應用時再做實際分析與研究,歡迎各位補充。時間倉促,措辭不當之處。

1、相關Tools

       YSlow、FireBug、JSA腳本壓縮工具、Yahoo腳本壓縮工具,

2、推薦相關書籍

       《構建高性能可擴展ASP.NET網站》Richard Kiessig著, 餘昭輝譯

       《構建高性能Web站點》 郭欣著


 

§、引用參考

提高網站的呈現速度其實重點在頁面上,改進方法如下:

一、我們需要解決的麻煩

* 減少HTTP請求數. 減少HTTP請求數有什麼好處
o 降低服務器跟客戶端的建立和消除HTTP請求和響應Header的開銷
o 減少服務器爲HTTP連接的進程和線程的開銷,如果可能,還會包括GZIP壓縮的CPU開銷.
* 減小被請求文件大小, 減少請求數據佔用的網絡帶寬.
* 讓用戶更快的看到想要的結果.
* 提高客戶端渲染速度.
* 讓瀏覽器同時能請求更多的數據.
* 提高服務器相應速度.
* 通過版本化控制客戶端Cache.

二、如何解決我們的麻煩

A.如何減少HTTP請求數
1. 合併JS文件跟CSS文件。
2. 合併框架圖片以及相對變動較少的圖片成一張,通過CSS背景切割來完成渲染,比如:加速圖片顯示。
3. 合理使用本地Cache來緩存JS/CSS/IMAGE。
4. 合理使用UserData緩存JS文件,對於FF用戶可以單獨請求服務器,這樣能解決80%用戶的問題.代碼可以蓉兒(meizz)的js framework1(標註1)。
5. 把JS跟CSS合併成一個文件

B.減小被請求文件大小,減少請求數據佔用的網絡帶寬
1. 壓縮JS體積:刪除JS中空白換行,註釋,混淆把長變量換成短變量;
2. 壓縮CSS體積:刪除CSS註釋、寫法儘量用簡寫;
3. 使用(X)HTML+CSS方式搭建網站結構,提高CSS重用性,來減少(X)HTML文件大小;
4. 使用服務器端GZIP壓縮JS/ CSS文件,縮小傳輸文件大小。附註:Apache1跟Apache2的GZIP的效率跟方式不一樣的,根據需要自行選擇。
嗷嗷補充說明:壓縮、合併JS和CSS都由程序處理。而不是自己手動去縮刪,不然不利於後期維護。

C.讓用戶更快的看到想要的結果
用戶對於一個站點的白頁的忍受時間根據統計是8-12秒。白頁的產生可能由於各種原因引起,我們能做的就是怎麼讓用戶能變的稍微能等待更久。
1. 方案1:多做一個引導頁,讓用戶體會其中的變化
    案例:mail.aol.com中的loading引導頁
2. 方案2:優先載入頁面結構以及結構圖片,後一步載入當前頁面數據,再後一步載入Iframe,Flash等數據.讓用戶儘早的看到被打開頁面的希望.

D.提高客戶端渲染速度

這個問題就比較泛泛了,影響客戶端的渲染速度有多方面的,主要目的都是提高程序方面的效率.
1. 對於大索引的結構,儘可能的少用索引訪問,能用訪問兄弟節點的方式儘可能用訪問兄弟節點的方式.
2. 字符串拼接儘可能用數組方式
3. 大規模添加節點數據,請不要使用appendChild方式,儘量使用類似innerHTML的insertAdjacentHTML方式,FF下需修正(標註1)

E.讓瀏覽器同時能請求更多的數據.
瀏覽器默認只是支持單域名同時有兩個HTTP請求,使用多域名將能把請求數提高,在網絡條件優良的情況下,能更快的下載數據,呈現結果.

F.提高服務器相應速度
對於需快速響應的文件,把其放入快速響應的服務器,應該是不錯的方案,優化方案請系統儲備組提供.

G.通過版本化控制客戶端Cache
通常js/css這類文件改動比較頻繁,但是爲了加載速度變快,我們有可能需要設定這類文件的過期時間爲幾天後,這樣我們碰到的問題就是,如何及時更新這些在cache的文件?
通過一個簡單的配置,通過修改JS的版本來及時告訴瀏覽器,這些文件必須重新請求了,不要繼續使用瀏覽器cache中的數據. 方案有好幾個:

1. 手動改這些js的文件名
2. 手動改這些js的路徑
3. 通過URL Rewrite方式來改重定位js路徑
4. 通過一個在高響應服務器上的一個js配置告知頁面,這個頁面該鏈接哪些JS文件
5. 大版本不變,小版本不斷追加,等一定時間後,統一更新,高效利用cache

標註
1. meizz的js framework還沒出正式版,有興趣在CSDN的頁面翻一下
2. Firefox修正方式

function addHTML(oParentNode, sHTML) { 
if(window.addEventListener) {// for MOZ 
var oRange = oParentNode.ownerDocument.createRange(); 
oRange.setStartBefore(oParentNode); 
var oFrag = oRange.createContextualFragment(sHTML); 
oParentNode.appendChild(oFrag); 
} 
else {// for IE5+ 
oParentNode.insertAdjacentHTML("BeforeEnd", sHTML); 
} 
} 


裝上述方法重構頁面,可以提高200—400%的呈現速度!記住減少“http握手數”,控制在11個握手數之內,可以用firefox 的firebug插件的Net功能查看http請求數!非頁面邏輯的JS放在頁面尾部!

2、second

提高ASPX服務器性能的幾大狠招

∆ 第0招依靠測試工具,以下根據ACT test測試結果,整理。【全部招數兇狠度的依據】

所謂性能優化,必須是建立在測試的基礎之上的,ACT Test是比較爽的測試工具,比Load Runner方便,比Web Stress直觀,支持腳本編程和錄製登陸到註銷全過程。
所有優化都要進行對比測試,纔是評判的數字依據。
所以,個人認爲:不做壓力測試,優化是可以做,但是沒數據支持,是不嚴謹的。

∆ 第一招生成靜態。【兇狠度排名第一:性能RPS提升兩個數量級(提高速度百倍)】

以下是截取Response的Stream生成文件的代碼

protected override void Render(HtmlTextWriter writer) 
{ 
StringWriter stringWriter = new StringWriter(); 
HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter); 
base.Render(htmlTextWriter); 

if (本頁允許生成靜態HTML) 
{ 
//根據aspx的get參數構造出的HTML文件名,如:aaa.aspx?id=1 轉化成 aaa_id_1.html 
string filePath = Server.MapPath(htmlFileName); 

StreamWriter streamWriter = new StreamWriter(filePath , false, Encoding.UTF8); 

streamWriter.Write(HTML); 

streamWriter.Close(); 

htmlTextWriter.Close(); 
} 
} 


生成後,以後先判斷是否有這個文件,如果有就跳過去。
本頁允許生成靜態HTML,可以在後臺做一個鉤選和更新,並且在內容發生變化後刪除靜態HTML。

∆ 第二招,圖片文件分流服務器。【兇狠度排名第二:性能RPS提升1個數量級(提高速度十倍)】


1,web 1臺或多臺。
2,圖片1臺或多臺。
3,文件1臺或多臺。
4,數據庫1臺或多臺。
不同機房要採用Remoting分發文件是個不錯的主義,就是工作量大。
以下是同機房多服務器之間可以通過web讀寫分發文件的關鍵代碼。

using System; 
using System.Collections; 
using System.Configuration; 
using System.Data; 
using System.IO; 
using System.Web; 
using System.Web.Security; 
using System.Web.UI; 
using System.Web.UI.HtmlControls; 
using System.Web.UI.WebControls; 
using System.Web.UI.WebControls.WebParts; 
using System.Security.Principal; 
using System.Runtime.InteropServices; 

namespace WebApplication1 
{ 
public partial class _Default : System.Web.UI.Page 
{ 
public const int LOGON32_LOGON_INTERACTIVE = 2; 
public const int LOGON32_PROVIDER_DEFAULT = 0; 

WindowsImpersonationContext impersonationContext; 

[DllImport("advapi32.dll", CharSet = CharSet.Auto)] 
public static extern int LogonUser(String lpszUserName, 
String lpszDomain, 
String lpszPassword, 
int dwLogonType, 
int dwLogonProvider, 
ref IntPtr phToken); 
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
public extern static int DuplicateToken(IntPtr hToken, 
int impersonationLevel, 
ref IntPtr hNewToken); 
private bool impersonateValidUser(String userName, String domain, String password) 
{ 

IntPtr token = IntPtr.Zero; 
IntPtr tokenDuplicate = IntPtr.Zero; 

if (LogonUser(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0) 
{ 
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) 
{ 
WindowsIdentity tempWindowsIdentity; 
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); 
impersonationContext = tempWindowsIdentity.Impersonate(); 
if (impersonationContext != null) 
return true; 
else 
return false; 
} 
else 
return false; 
} 
else 
return false; 
} 
private void undoImpersonation() 
{ 
impersonationContext.Undo();//回退爲未更改前賬戶 
} 
protected void Page_Load(object sender, EventArgs e) 
{ 
//臨時更改爲 跟 網絡硬盤相同用戶名密碼的賬戶(此賬戶必須在網絡盤有寫入權限)本機也需要同樣帳號密碼的帳戶 
if (impersonateValidUser("administrator", "192.168.1.102", "kuqu123456")) 
{ 
Response.Write(System.IO.File.Exists(@"//192.168.1.102/share/C#高級編程/C#高級編程(第四版).pdf")); 
undoImpersonation(); 
} 
else 
{ 
Response.Write("登陸失敗"); 
} 

} 
} 
} 


∆ 第三招,各種緩存。【兇狠度排名第三:性能RPS提升1個數量級(提高速度十倍級別)】
   緩存代碼好找,就不寫了,道理不用多講。

∆ 第四招,減少數據庫往返。【兇狠度排名第四:性能RPS提升和往返次數有關】
能一次取的數據,就不要多次,尤其在綁定的行裏多次執行存儲過程。

∆ 第五招,少用對象。【兇狠度排名第五:性能RPS提升取決於對象數量】
用Static函數執行SQL,綁定數據或者增刪查改,來代替 new一堆對象去綁定List或者設置屬性。

∆ 第六招,多用Ajax和客戶端HTML代碼,少用服務器控件,減少刷新和ViewState。【兇狠度排名第六:性能RPS提升取決於具體界面功能】
道理不用多講,服務器控件垃圾比較多。

∆ 第七招,採用好的數據結構容器和算法。。【兇狠度排名第七:性能提升取決於算法複雜度,普通Web開發這種情況不多見,但是特殊開發要重視!~~】
哈希類性能最好,查找複雜度是O(1),例如:Dictionnary <T,T>,
二叉樹查找複雜度是O(Log n),排序O(n*Log n),List和ArraList自帶的排序速度是接近(n*Log n),
數組類的線性查找O(n),如果你用冒泡排序O(n*n),那麼你不是優秀程序員。
StringBuilder 性能好於 string+=,不過提升的有限,不是本質的數量級別的提升。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章