前言
網站設計的優化是一個很大的話題,有一些通用的原則,也有針對不同開發平台的一些建議。這方面的研究一直沒有停止過,我在不同的場合也分享過這樣的話題。
作為通用的原則,雅虎的工程師團隊曾經給出過35個最佳實踐。這個列表請參考 Best Practices for Speeding Up Your Web Site http://developer.yahoo.com/performance/rules.html,同時,他們還發布了一個相應的測試工具Yslow http://developer.yahoo.com/yslow/
我強烈推薦所有的網站開發人員都應該學習這些最佳實踐,並結合自己的實際項目情況進行應用。 接下來的一段時間,我將結合ASP.NET這個開發平台,針對這些原則,通過一個系列文章的形式,做些講解和演繹,以幫助大家更好地理解這些原則,並且更好地使用他們。
准備工作
為了跟隨我進行后續的學習,你需要准備如下的開發環境和工具
- Google Chrome 或者firefox ,並且安裝 Yslow這個擴展組件.請注意,這個組件是雅虎提供的,但目前沒有針對IE的版本。
- https://chrome.google.com/webstore/detail/yslow/ninejjcohidippngpapiilnmkgllmakh
- https://addons.mozilla.org/en-US/firefox/addon/yslow/
- 你應該對這些瀏覽器的開發人員工具有所了解,你可以通過按下F12鍵調出這個工具。
- Visaul Studio 2010 SP1 或更高版本,推薦使用Visual Studio 2012
- 你需要對ASP.NET的開發基本流程和核心技術有相當的了解,本系列文章很難對基礎知識做普及。
本文要討論的話題
這一篇我和大家討論第十一條原則:Avoid Redirects (避免重定向)。
重定向的意思是,用戶的原始請求(例如請求A)被重定向到其他的請求(例如請求B)。這是HTTP世界中本來就存在的技術和現象,它本身沒有所謂的好和壞,它的存在也確實有其理由,為此HTTP協議中,規定了兩個狀態碼來標識這種場景。它們分別是:
-
301 Moved Permanently , 這個狀態碼標識用戶所請求的資源被移動到了另外的位置,客戶端收到此響應后,需要發起另外一個請求去下載所需的資源。這個狀態碼,日后可能會和另外一個狀態碼 308 (308 Permanent Redirect (approved as experimental RFC)[12])有些關聯(這個還沒有最終確定為標准)
-
302 Found ,這個狀態碼標識用戶所請求的資源被找到了,但不在原始位置,服務器會回復其他的一個地址,客戶端收到此響應后,也需要發起另外一個請求去下載所需的資源。這個狀態碼,日后可能會和另外兩個狀態碼有關聯。
-
303 See Other (since HTTP/1.1)
-
307 Temporary Redirect (since HTTP/1.1)
-
目前,我們一直只要區分301和302即可。它們本質上的區別到底是什么呢?其實也不難:301表示永久重定向,302表示臨時重定向。對於一般的用戶而言,可能你還無法體會出來他們的區別,因為橫豎都是要重定向的。但對於搜索引擎而言意義就非凡。我們都知道,搜索引擎是需要不定期對網站資源進行爬網,以便完善對應的索引結構的。當某個資源被永久重定向(301),搜索引擎會聰明地知道,在索引中應該記錄就是永久重定向之后的新地址,而不是老地址,這樣就可以避免用戶通過搜索引擎來查詢的時候,每次還需要先到老地址,再重定向到新地址。而對於臨時重定向(302),則不會這么做。
重定向會有什么影響
說了這么多理論知識,我們還是通過一個實例來看看具體重定向是如何發生的吧
我們首先請求的是default.aspx頁面,出於某種原因,這個請求需要被重定向到另外一個頁面,Product.aspx。所以,服務器首先為default.aspx這個請求返回302的狀態碼,表示說這里需要臨時重定向。然后,在響應的頭部(Header)中,還包含了新的地址:Product.aspx。(如下圖所示)。在響應的正文(Body)中,則是空白的。
瀏覽器收到了這個回復之后,再重新發起一個請求,Product.aspx,這個請求被正常地處理了,返回200的狀態碼。
事情就是這樣。這樣的問題在於:這樣做顯然增加了瀏覽器到服務器的往返次數,這違背了另外一個原則:Make Fewer HTTP Requests 。
重定向是如何發生的
事實上,重定向是經常發生的。有兩種主要的情況下會發生重定向
- 服務器本身的一些行為(針對某類請求)
- 程序中明確地做了重定向
第一種情況很有意思,不同的服務器可能在處理的時候表現也是不一樣的。例如我們來看下面這個請求
通過監控我們發現,首先會有一個301的重定向,然后才是真正的請求(返回200),此時地址是 http://www.cnblogs.com
這是為什么呢?如果有網站開發經驗的朋友一定知道,域名都是需要解析的,實際上向用戶提供的服務,是互聯網上面的某個網絡主機。做得比較好的網站,同時會考慮用戶希望訪問網站的不同的方式,上例中,用戶既可以訪問cnblogs.com 也可以訪問www.cnblogs.com,區別在於前者多一次重定向請求。
如果你就此認為是理所應當的,那么你就錯了。我們再來看一下“著名”的12306.cn 吧
你如果這樣訪問的時候,直接就失敗了。你必須完整地輸入www.12306.cn 才能訪問。試問,這是多么簡單、低級的問題。
其實要做到這個很簡單,犧牲了一點點性能,對於用戶來說,提高了用戶體驗。這里留一個疑問:有人可以解釋一下,到底如何實現這樣的效果(cnblogs.com => www.cnblogs.com )嗎?
這是一種典型的重定向,這個無需任何代碼就能實現。實際上是屬於服務器的功能。當然,我們完全應該盡可能地使用www.cnblogs.com 這種方式(例如給人們郵件中的鏈接,盡量是使用這個路徑)。但提供了另外一個方式,是很好的設計。
還有一種典型的重定向,我們來看下面這個例子,請在瀏覽器中輸入google.com
我們最終看到的頁面是 http://www.google.com.hk/
你會發現,會有幾次重定向
第一次重定向是301,從google.com 重定向到www.google.com
第二次重定向是302,從www.google.com 重定向到 http://www.google.com.hk/url?sa=p&hl=zh-CN&pref=hkredirect&pval=yes&q=http://www.google.com.hk/&ust=1367722843791916&usg=AFQjCNEzkTX2uE5Jlo3NkA1vSHdwoCnnZQ
第三次重定向是302,從http://www.google.com.hk/url?sa=p&hl=zh-CN&pref=hkredirect&pval=yes&q=http://www.google.com.hk/&ust=1367722843791916&usg=AFQjCNEzkTX2uE5Jlo3NkA1vSHdwoCnnZQ 重定向到 http://www.google.com.hk
同樣的做法,我們從bing.com中也能看到。我們輸入bing.com ,但實際看到的頁面是cn.bing.com
實際上,這里也發生了一次重定向
為什么Google和Microsoft都會這樣設計搜索引擎的主界面呢?原因在於他們想給用戶提供更加有個性化的本地服務,所以針對不同國家和地區的用戶,實際上有獨立的主機來進行處理。
Microsoft的做法有點不一樣,他們使用了統一的域名bing.com,只是為不同的國家和地區准備了不同的主機,例如cn.bing.com, hk.bing.com 。
還有一種容易比我們忽視的重定向。請參考下面的實例
在這個示例網站中,有一個文件夾,叫做Products,里面會有很多頁面,例如至少會有一個Default.aspx。我們都知道,通常Default.aspx是所謂的默認頁面,也就是說,要訪問這個頁面的話,並不需要輸入Default.aspx這個部分,而是直接通過訪問文件夾名稱即可,例如下面這樣
http://localhost:9071/Products
和你想象的一樣,這樣的簡寫路徑會返回Products目錄中的Default.aspx頁面的內容(這很不錯,對吧)但是,通過監控我們發現,這樣一個請求都會發生了一次重定向
有意思的是,它會將地址重定向到 http://localhost:9071/Products/ (只是比原始地址多了一個路徑斜線),很神奇嗎?但這是真的,凡是訪問地址中,沒有帶文件名后綴的(例如aspx,asp等等),服務器都會嘗試解析為一個文件夾,自動加上一個路徑斜線,然后再查找內部的默認頁面。
【備注】這個行為,在ASP.NET MVC中是不會存在的,因為ASP.NET MVC的請求處理是被路由處理的。
ASP.NET中的重定向
如果上述網站是用ASP.NET開發的(cnblogs.com 顯然是的,而Google.com則顯然不是的,bing.com 會是的嗎?),那么會怎么樣來實現上述所提到的重定向呢?
ASP.NET 4.0 有如下幾種方式來做重定向
- Response.Redirect http://msdn.microsoft.com/EN-US/library/08za4s98(v=VS.110,d=hv.2).aspx
- 這個會進行臨時重定向,也就是返回302
- Response.RedirectPermanent http://msdn.microsoft.com/EN-US/library/dd322058(v=VS.110,d=hv.2).aspx (這是新增的方法)
- 這個會進行永久性重定向,也就是返回301
- Server.Transfer http://msdn.microsoft.com/EN-US/library/y0w8173d(v=VS.110,d=hv.2).aspx
- 這個不會產生301,也不會產生302,實際上,它直接返回200,瀏覽器根本不會知道發生了重定向,不會有多出來的一個請求
- 聽起來不錯,不是嗎?那么,請問,這樣做有什么附加的問題嗎?
- 這個不會產生301,也不會產生302,實際上,它直接返回200,瀏覽器根本不會知道發生了重定向,不會有多出來的一個請求
ASP.NET中重定向附加的問題
以上的三個方法,我相信大家至少對其中一兩個很熟悉,但是很多人不清楚他們的代價。
實際上,不管是用Redirect還是Transfer方法,他們內部都會調用Response.End方法(這個很好理解,因為需要重定向了,所以當前的這個請求應該就不需要再提供響應了),但這個方法會導致與該請求有關的處理線程強制被中斷掉,具體來說,這將引發一個異常(ThreadAbortException),通過Try…catch就能捕捉到。
我們都知道,異常處理在.NET中是由CLR來做的,異常處理的代價是較高的,所以如果過於頻繁地拋出異常,會給性能帶來顯著的影響。
關於這個問題,以及如何改善,可以參考微軟官方的文檔
http://support.microsoft.com/kb/312629/en-us
我摘錄解決方案的部分如下
To work around this problem, use one of the following methods:
- For Response.End, call the HttpContext.Current.ApplicationInstance.CompleteRequest method instead of Response.End to bypass the code execution to the Application_EndRequest event.
- For Response.Redirect, use an overload, Response.Redirect(String url, bool endResponse) that passes false for theendResponse parameter to suppress the internal call to Response.End. For example:
Response.Redirect ("nextpage.aspx", false);
If you use this workaround, the code that follows Response.Redirect is executed.
- For Server.Transfer, use the Server.Execute method instead.
如何盡可能避免重定向
從上面的實例和分析來看,重定向是無法完全避免的,適當地使用重定向能為網站提供更好的功能。(例如本地化,用戶體驗等方面)。
但是過多地進行重定向也肯定會給網站性能帶來顯著的影響。那么,有哪些方法可以作為我們改善這一點的參考呢
- 在定義鏈接地址的href屬性的時候,盡量使用最完整的、直接的地址。例如
- 使用www.cnblogs.com 而不是cnblogs.com
- 使用cn.bing.com 而不是bing.com
- 使用www.google.com.hk 而不是google.com
- 使用www.mysite.com/products/ 而不是 www.mysite.com/products
- 在使用Response.Redirect的時候,設置第二個參數為false
- 考慮是否可用Server.Execute代替
- 考慮Respone.RedirectPermanent
- 如果涉及到從測試環境到生產環境的遷移,建議通過DNS中的CNAME的機制來定義別名,而不是強制地重定向來實現