大型網站架構改進歷程(9):網站靜態化處理–總述(1)

夏天的森林發表於2015-02-12

在儲存瓶頸的開篇我提到像hao123這樣的導航網站只要它部署的web伺服器數量足夠,它可以承載超大規模的併發訪問量,如果是一個動態的網站,特別是使用到了資料庫的網站是很難做到通過增加web伺服器數量的方式來有效的增加網站併發訪問能力的。但是現實情況是像淘寶、京東這樣的大型動態網站在承擔高併發的情況下任然能保證快速的響應,這其中有什麼樣的技術手段可以達到動態網站支撐高併發的場景了,這也許是每個做web開發的朋友都很感興趣的問題,今天我將寫一個新的系列來探討下這個問題,希望我的經驗和研究能給大多數人以啟迪。這裡要說明下,本系列的寫法和儲存的瓶頸的寫法有所不同,本系列開始部分主要是講解原理,後面部分會針對原理講解具體的實現手段,如果有朋友感覺這種寫法不適應,還請諒解。

我個人總結下來,這些大型動態網站之所以可以做到能快速響應高併發,它們都是儘量讓自己的網站靜態化,當然這種靜態化絕不是把網站就做成靜態網站,而是在充分理解了靜態網站在提升網站響應速度的基礎上對動態網站進行改良,所以我這裡首先要討論下靜態網站那些特點可以用於我們提升網站的響應速度。

靜態網站非常簡單,它就是通過一個url訪問web伺服器上的一個網頁,web伺服器接收到請求後在網路上使用http協議將網頁返回給瀏覽器,瀏覽器通過解析http協議最終將頁面展示在瀏覽器裡,有時這個網頁會比較複雜點,裡面包含了一些額外的資源例如:圖片、外部的css檔案、外部的js檔案以及一些flash之類的多媒體資源,這些資源會單獨使用http協議把資訊返回給瀏覽器,瀏覽器從頁面裡的src,href、Object這樣的標籤將這些資源和頁面組合在一起,最終在瀏覽器裡展示頁面。但是不管什麼型別的資源,這些資源如果我們不是手動的改變它們,那麼我們每次請求獲得結果都是一樣的。這就說明靜態網頁的一個特點:靜態網頁的資源基本是不會發生變化的。因此我們第一次訪問一個靜態網頁和我們以後訪問這個靜態網頁都是一個重複的請求,這種網站載入的速度基本都是由網路傳輸的速度,以及每個資源請求的大小所決定,既然訪問的資源基本不會發生變化,那麼我們重複請求這些資源,自己在那裡空等不是很浪費時間嗎?如是乎,瀏覽器出現了快取技術,我們開發時候可以對那些不變的資源在http協議上編寫相應指令,這些指令會讓瀏覽器第一次訪問到靜態資源後快取起這些靜態資源,使用者第二次訪問這個網頁時候就不再需要重複請求了,因為請求資源本地快取,那麼獲取它的效率就變得異常高效。

由於靜態網站的請求資源是不會經常發生變化的,那麼這種資源其實很容易被遷移,我們都知道網路傳輸的效率是和距離長短有關係的,既然靜態資源很容易被遷移那麼我們就可以把靜態資源伺服器按地域分佈在多個服務節點上,當使用者請求網站時候根據一個路由演算法將請求落地在離使用者最近的節點上,這樣就可以減少網路傳輸的距離從而提升訪問的效率,這就是我們長提的大名鼎鼎的CDN技術,內容分發網路技術。

網路傳輸效率還和我們傳輸資源的大小有關,因此我們在資源傳輸前將其壓縮,減小資源的大小從而達到提升傳輸效率的目的;另外,每個http請求其實都是一個tcp的請求,這些請求在建立連線和釋放連線都會消耗很多系統資源,這些效能的消耗時常會比傳輸內容本身還要大,因此我們會盡力減少http請求的個數來達到提升傳輸效率的目的或者使用http長連線來消除建立連線和釋放連線的開銷(長連線的使用要看具體場景,這個我會在後面文章講到)。

其實雅虎提出的網站優化的14條建議大部分都是基於以上原理得出的,關於雅虎的14條件建議,本系列後面內容將做詳細的討論,這裡就不展開了。

我常常認為最佳的效能優化手段就是使用快取了,但是快取的資料一般都是那些不會經常變化的資料,上文裡說到的瀏覽器快取,CDN其實都是可以當做快取手段來理解,它們也是提升網站效能最為有效的方式之一,但是這些快取技術到了動態網站卻變得異常不好實施,這到底是怎麼回事了?

首先動態網站和靜態網站有何不同呢?我覺得動態網站和靜態網站的區別就是動態網站網頁雖然也有一個url,但是我們如果傳輸引數不同那麼這個url請求的頁面並不是完全一樣,也就是說動態網站網頁的內容根據條件不同是會發生改變的,但是這些變化的內容卻是同一個url,url在靜態網站裡就是一個資源的地址,那麼在動態網站裡一個地址指向的資源其實是不同的。因為這種不同所以我們沒法把動態的網頁進行有效的快取,而且不恰當的使用快取還會引發錯誤,所以在動態網頁裡我們會在meta設定頁面不會被瀏覽器快取。

如果每次訪問動態的網頁該網頁的內容都是完全不同的,也許我們就沒有必要寫網站靜態化的主題了,現實中的動態網頁往往只是其中一部分會發生變化,例如電商網站的選單、頁面頭部、頁面尾部這些其實都不會經常發生變化,如果我們只是因為網頁一小部分經常變化讓使用者每次請求都要重複訪問這些重複的資源,這其實是非常消耗計算資源了,我們來做個計算吧,假如一個動態頁面這些不變的內容有10k,該網頁一天有1000萬次的訪問量,那麼每天將消耗掉1億kb的網路資源,這個其實很不划算的,而且這些重複消耗的寬頻資源並沒有為網站的使用者體驗帶來好處,相反還拖慢了網頁載入的效率。那麼我們就得考慮拆分網頁了,把網頁做一個動靜分離,讓靜態的部分當做不變的靜態資源進行處理,動態的內容還是動態處理,然後在合適的地方將動靜內容合併在一起。

這裡有個關鍵點就是動靜合併的位置,這個位置的選擇會直接導致我們整個web前端的架構設計。我們這裡以java的web開發為例,來談談這個問題。

java的web開發裡我們一般使用jsp來編寫頁面,當然也可以使用先進點的模板引擎開發頁面例如velocity,freemark等,不管我們頁面使用的是jsp還是模板引擎,這些類似html的檔案其實並不是真正的html,例如jsp本質其實是個servlet也就是一個java程式,所以它們的本質是服務端語言和html的一個整合技術,在實際執行中web容器會根據服務端的返回資料將jsp或模板引擎解析成瀏覽器能解析的html,然後傳輸這個html到瀏覽器進行解析。由此可見服務端語言提供的開發頁面的技術其實是動靜無法分離的源頭,但是這些技術可以很好的完成動靜資源中的動的內容,因此我們想做動靜分離那麼首先就要把靜的資源從jsp或者模板語言裡抽取出來,抽取出來的靜態資源當然就要交給靜態的web伺服器來處理,我們常用的靜態資源伺服器一般是apache或ngnix,所以這些靜態資源應該放置在這樣的伺服器上,那麼我們是否可以在這些靜態web伺服器上做動靜結合呢?答案是還真行,例如apache伺服器有個模組就可以將它自身儲存的靜態資源和服務端傳輸的資源整合在一起,這種技術叫做ESI、CSI,這個時候我們可以把不變的靜態內容製作成模板放置在靜態伺服器上,動態內容達到靜態資源伺服器時候,使用ESI或者CSI的標籤,把動靜內容結合在一起,這就完成了一個動靜結合操作。這裡就有一個問題了,我前面提到過CDN,CDN其實也是一組靜態的web伺服器,那麼我們是否可以把這些事情放到CDN做了?理論上是可以做到,但是現實卻是不太好做,因為除了一些超有錢的網際網路公司,大部分公司使用的CDN都是第三方提供的,第三方的CDN往往是一個通用方案,再加上人家畢竟不是自己人,而且CDN的主要目的也不是為了做動靜分離,因此大部分情況下在CDN上完成這類操作並不是那麼順利,因此我們常常會在服務端的web容器前加上一個靜態web伺服器,這個靜態伺服器起到一個反向代理的作用,它可以做很多事情,其中一件事情就是可以完成這個動靜結合的問題。

那麼我們把這個動靜結合點再往前推,推到瀏覽器,瀏覽器能做到這件事情嗎?如果瀏覽器可以,那麼靜態資源也就可以快取在客戶端了,這比快取在CDN效率還要高,其實瀏覽器還真的可以做到這點,特別是ajax技術出現後,瀏覽器來整合這個動靜資源也就變得更加容易了。不過一般而言,我們使用ajax做動靜分離都是都是從服務端請求一個html片段,到了瀏覽器後,使用dom技術將這個片段整合到頁面裡,雖然這個已經比全頁面返回高效很多,但是他還是有問題的,服務端處理完請求最終返回結果其實都是很純粹的資料,可是這些資料我們不得不轉化為頁面片段返回給瀏覽器,這本質是為純粹的資料上加入了很多與服務端無用的結構,之所以說無用是因為瀏覽器自身也可以完成這些結構,為什麼我們一定要讓服務端做這個事情了?如是乎javascript的模板技術出現了,這些模板技術和jsp,velocity類似,只不過它們是通過javascript設計的模板語言,有了javascript模板語言,服務端可以完全不用考慮對頁面的處理,它只需要將有效的資料返回到頁面就行了,使用了javascript模板技術,可以讓我們動靜資源分離做的更加徹底,基本上所有的瀏覽器相關的東西都被靜態化了,服務端只需要把最原始的資料傳輸到瀏覽器即可。講到這裡我們就說到了web前端最前沿的技術了:javascriptMVC架構了。

好了今天就寫到這裡,本篇文章是網站靜態化處理理論的總述,後面的文章我將會一點一滴的講述實現網站靜態化的各種技術實現細節。

相關文章