在存儲瓶頸的開篇我提到像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,這個時候我們可以把不變的靜態內容制作成模板放置在靜態服務器上,動態內容達到靜態資源服務器時候,使用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架構了。
好了今天就寫到這里,本篇文章是網站靜態化處理理論的總述,后面的文章我將會一點一滴的講述實現網站靜態化的各種技術實現細節。
文章列表