關於大型網站技術演進的思考

網站靜態化處理--總述(1)

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

  我我的總結下來,這些大型動態網站之因此能夠作到能快速響應高併發,它們都是儘可能讓本身的網站靜態化,固然這種靜態化毫不是把網站就作成靜態網站,而 是在充分理解了靜態網站在提高網站響應速度的基礎上對動態網站進行改良,因此我這裏首先要討論下靜態網站那些特色能夠用於咱們提高網站的響應速度。php

  靜態網站很是簡單,它就是經過一個url訪問web服務器上的一個網頁,web服務器接收到請求後在網絡上使用http協議將網頁返回給瀏覽器,瀏覽 器經過解析http協議最終將頁面展現在瀏覽器裏,有時這個網頁會比較複雜點,裏面包含了一些額外的資源例如:圖片、外部的css文件、外部的js文件以 及一些flash之類的多媒體資源,這些資源會單獨使用http協議把信息返回給瀏覽器,瀏覽器從頁面裏的src,href、Object這樣的標籤將這 些資源和頁面組合在一塊兒,最終在瀏覽器裏展現頁面。可是無論什麼類型的資源,這些資源若是咱們不是手動的改變它們,那麼咱們每次請求得到結果都是同樣的。 這就說明靜態網頁的一個特色:靜態網頁的資源基本是不會發生變化的。所以咱們第一次訪問一個靜態網頁和咱們之後訪問這 個靜態網頁都是一個重複的請求,這種網站加載的速度基本都是由網絡傳輸的速度,以及每一個資源請求的大小所決定,既然訪問的資源基本不會發生變化,那麼咱們 重複請求這些資源,本身在那裏空等不是很浪費時間嗎?如是乎,瀏覽器出現了緩存技術,咱們開發時候能夠對那些不變的資源在http協議上編寫相應指令,這 些指令會讓瀏覽器第一次訪問到靜態資源後緩存起這些靜態資源,用戶第二次訪問這個網頁時候就再也不須要重複請求了,由於請求資源本地緩存,那麼獲取它的效率 就變得異常高效。css

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

  網絡傳輸效率還和咱們傳輸資源的大小有關,所以咱們在資源傳輸前將其壓縮,減少資源的大小從而達到提高傳輸效率的目的;另外,每一個http請求其實都 是一個tcp的請求,這些請求在創建鏈接和釋放鏈接都會消耗不少系統資源,這些性能的消耗時常會比傳輸內容自己還要大,所以咱們會盡力減小http請求的 個數來達到提高傳輸效率的目的或者使用http長鏈接來消除創建鏈接和釋放鏈接的開銷(長鏈接的使用要看具體場景,這個我會在後面文章講到)。前端

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

  我經常認爲最佳的性能優化手段就是使用緩存了,可是緩存的數據通常都是那些不會常常變化的數據,上文裏說到的瀏覽器緩存,CDN其實都是能夠當作緩存手段來理解,它們也是提高網站性能最爲有效的方式之一,可是這些緩存技術到了動態網站卻變得異常很差實施,這究竟是怎麼回事了?java

  首先動態網站和靜態網站有何不一樣呢?我以爲動態網站和靜態網站的區別就是動態網站網頁雖然也有一個url,可是咱們若是傳輸參數不一樣那麼這個url請求的頁面並非徹底同樣,也就是說動態網站網 頁的內容根據條件不一樣是會發生改變的,可是這些變化的內容倒是同一個url,url在靜態網站裏就是一個資源的地址,那麼在動態網站裏一個地址指向的資源 實際上是不一樣的。由於這種不一樣因此咱們無法把動態的網頁進行有效的緩存,並且不恰當的使用緩存還會引起錯誤,因此在動態網頁裏咱們會在meta設定頁面不會 被瀏覽器緩存。node

  若是每次訪問動態的網頁該網頁的內容都是徹底不一樣的,也許咱們就沒有必要寫網站靜 態化的主題了,現實中的動態網頁每每只是其中一部分會發生變化,例如電商網站的菜單、頁面頭部、頁面尾部這些其實都不會常常發生變化,若是咱們只是由於網 頁一小部分常常變化讓用戶每次請求都要重複訪問這些重複的資源,這實際上是很是消耗計算資源了,咱們來作個計算吧,假如一個動態頁面這些不變的內容有 10k,該網頁一天有1000萬次的訪問量,那麼天天將消耗掉1億kb的網絡資源,這個其實很不划算的,並且這些重複消耗的寬帶資源並無爲網站的用戶體 驗帶來好處,相反還拖慢了網頁加載的效率。那麼咱們就得考慮拆分網頁了,把網頁作一個動靜分離,讓靜態的部分當作不變的靜態資源進行處理,動態的內容仍是 動態處理,而後在合適的地方將動靜內容合併在一塊兒。nginx

  這裏有個關鍵點就是動靜合併的位置,這個位置的選擇會直接致使咱們整個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架構了。

 

網站靜態化處理—動靜整合方案(2)

上篇文章我簡要的介紹了下網站靜態化的演進過程,有朋友可能認爲這些知識有點過於稀鬆日常了,並且網站靜態化的技術基點也不是那麼高深和難以理解, 所以它和時下突飛猛進的web前端技術相比,就顯得不三不四了。其實當我打算寫本系列的以前我我的以爲web前端有一個點是不少人都知道重要,可是有經常 低估它做用的,那就是web前端和web服務端如何融合的這個點上,這個點再加上咱們要作出一個規模龐大,高併發,快速響應的網站時候它對於web前端的架構技術的演進起到了一個不可忽視的做用。

  網站的web前端要實現高效,第一個要解決的短板就是網絡的延遲性對網站的加載效率的影響,固然不少人會說網速快不快這是網絡運營商的問題,不是網站 的問題,可是你們確定也見過就算咱們用上了千兆寬帶也會有些網站加載速度慢的讓人沒法忍受,網站自己的確是無法控制網絡速度的能力,可是若是咱們不下降網 絡對頁面加載效率的影響,其餘任何優化網站的手段也就無從談起,緣由就是網絡效率對於網頁加載效率的影響是起到大頭做用的,只有這個大頭被解決了,那麼解 決其餘的小頭才能發揮做用。

  回到上文講到的網站靜態化的關鍵點動靜分離,解決這個關鍵點的本質就是爲了下降網速對網站加載效率的影響,可是咱們在處理動靜分離問題時候採起的策略不一樣會對咱們整個網站架構產生重大影響,特別是將網頁作好動靜拆分後,靜態的資源盡力向瀏覽器端推移,這就致使了前端架構出 現了之前服務端纔有的MVC模式,這就致使web前端架構產生了質的變化,如是一些原來適用於flash這樣的重客戶端的技術也被傳統的web前端所採 用,MVC模式在web前端進一步演進由此而出現了MVP(Model-View-Presenter)模式,MVVM(Model-View- ViewModel)模式。也許上篇文章裏有人對講述動靜分離的原理有點異議,可是當今突飛猛進的web前端技術就是這些常見技術不斷演化而來,這就是我 上篇想表達的內容,我以爲這個系列的特色應該是細節,這是和上個系列存儲的瓶頸注重思想是有所不一樣的。

  動態網站最難以動靜分離的就是頁面了,其餘的靜態資源例如:圖片、外部腳本文件等等這些和靜態網站的 手法基本一致,其實業界很早就關注了動態網站的動靜分離問題,而且爲不一樣的動靜分離方案都進行了總結,今天我就介紹下這些技術。本人web服務端的工做語 言是java,所以下面服務端的例子是使用java的web技術闡述的,其餘語言例如php都有與之對應的技術,因此請那些不是使用java做爲服務端工 做語言的朋友能夠類比學習。

  在java的web開發裏,頁面技術jsp自己就包含了將頁面動靜分離的手段,例以下面的代碼:

1
2
3
4
5
6
7
<%@ include file=」header.<span style="width: auto; height: auto; float: none;" id="10_nwp"><a style="text-decoration: none;" mpid="10" target="_blank" href="http://cpro.baidu.com/cpro/ui/uijs.php?c=news&cf=1001&ch=0&di=128&fv=16&jk=8b18a605b896566a&k=jsp&k0=jsp&kdi0=0&luki=2&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=6a5696b85a6188b&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F5967%2Ehtml&urlid=0" id="10_nwl"><span style="color:#0000ff;font-size:14px;width:auto;height:auto;float:none;">jsp</span></a></span>」 %>
 
<body>
 
          ……….
 
<%@ include file=」footer.jsp」 %>

  通常一個網站的頭部和尾部都是同樣,所以咱們把頭部的代碼單獨放置在一個header.jsp頁面裏,頁面尾部的代碼放置下footer.jsp頁面 裏,這樣技術人員在開發頁面時候就再也不須要重複編寫這些重複的代碼,只須要引用便可,這個作法最大的好處就是能夠避免不一樣頁面在相同代碼這塊的不一致性, 假如沒有這個統一引用的話,手動編寫或者複製和粘貼,出錯的機率是很是的高的。

  可是這個作法有一個問題,問題就是這種動靜分離其實都是做用於單個頁面的,也就是說每一個頁面都要手動的重複這個動靜分離的操做,大多數狀況這種作法都不會有什麼問題,可是對於一個大型網站而言這種作法就有可能會製造沒必要要的麻煩,這裏我截取了一張京東的首頁,以下圖所示:

 

  講述前我要事先聲明下,京東網站可 能不存在我要講述的問題,我這裏只是使用京東網站的首頁作例子來講明,看圖裏的首頁和食品兩個條目,有些公司作這樣的網站時候這些導航進入的頁面會是一個 獨立的工程,每一個工程都是由獨立的項目組開發維護的,雖然項目組不一樣可是他們頁面的總體結構會是一致的,若是按照上面的動靜分離手段,那麼每一個項目組都要 獨立維護一份相同的頭部尾部資源,這個時候麻煩來了,若是該公司要新增個新的條目,那麼每一個項目組都要更新本身不變的資源,若是該企業一共分了5個項目 組,如今又作了一個新的條目,那麼其餘與之無關的項目組都得折騰一次更改統一引用文件的工做,要是作的不仔細就有可能出現頁面展現不一致的問題,爲了解決 這個問題,java的web開發裏就會考慮使用模板語言替代jsp頁面技術,例如模板語言velocity,這些模板語言都包含一個佈局的功能,例如velocity就有這樣的功能,咱們看看velocity的佈局模板實例,以下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<html>
 
<head>
 
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 
     <title>#springMessage("page_upop_title")</title>
 
     <meta http-equiv="X-UA-Compatible" content="requiresActiveX=true"/>
 
     <meta name="keywords" content='#springMessage("page_upop_keywords")'/>
 
     <meta content='#springMessage("page_upop_description")' name="description"/>
 
</head>
 
<body oncontextmenu="return false" onselectstart="return false">
 
     #if($pageHead)
 
         #parse($pageHead)
 
     #end
 
     $screen_content
 
     #parse($page_footer)
 
</body>
 
</html>

  頁面裏咱們能夠引入這個佈局格式,這個佈局文件其實就是頁面裏不變的東西抽取了出來,它完成了頁面動靜分離,頁面只要應用這個佈局文件便可,到了這裏這個佈局文件和前面的include方式區別不大,那麼咱們再看看下面的代碼:

1
<property name="layoutUrl" value="layout/default.vm"/><!--指定layout文件-->

  這是佈局文件的引用方式,咱們能夠把佈局文件放置在網絡上,項目裏應用這個文件所在地址便可,這樣咱們就把項目裏不變的靜態資源抽取在同一個地方,若是在碰到佈局要作修改,那麼咱們只須要改一個地方便可。

  無論服務端採起何種動靜分離,動靜資源的整合都是有服務端完成,按照上文提到網站靜態化的思想,這些作法不會給網站性能提高帶來任何好處,它們只是給 開發,運維提供了便利而已,按前文的思路,咱們要把動靜分離往前移,服務端往前移碰到的第一個點就是靜態的web服務器例如apache或ngnix。

  在講解靜態的web服務器動靜分離前我要先講一下爲何咱們要在服務端前面加個靜態web服務器的道理。我我的以爲在每一個服務端以前都佈置一個靜態web服務器,該服務器起到一個反向代理的做用,並且我以爲無論咱們是否使用CDN,最好都這麼作,這麼作有以下好處:

  好處一:方便日誌的記錄。

  好處二:在服務端以前設立了一個安全屏障,即靜態web服務器能夠在必要時候過濾有害的請求。

  好處三:能夠控制流入到服務端的請求個數,當併發很高時候,能夠利用靜態web服務器能承擔更高併發的能力來緩衝服務端的壓力,這 裏我補充一些實踐技巧,以java裏經常使用的web容器tomcat爲例,通常官方給出它的最大併發數應該不會超過200,若是咱們在tomcat前放置了 一個apache服務器,那麼咱們能夠把tomcat的最大併發數設置爲無效大,把併發數的控制放置在apache這邊控制,這麼作會給咱們系統運維帶來 很大的好處,tomcat雖然有一個建議最大併發數,可是實際運行裏java的web容器到底能承受多大併發其實要看具體場景了,所以咱們若是能夠動態控 制apache的併發數,這個操做很方便的,那麼咱們就能夠動態的調整tomcat這樣容器的承載能力。

  好處四:能夠便於咱們作動靜分離。

  這裏咱們以apache爲例子講解將動靜分離前移到apache的一些作法,apache有一個功能叫作SSI,英文全稱是Server Side Include,頁面上咱們通常這樣使用SSI,SSI有一種標籤,例如:

1
<!--#include file="info.htm"-->

  頁面通常使用註釋的方式引入,這個和jsp的 引入有點區別的,SSI的作法其實和服務端的引入相似,只不過使用SSI將原本服務端作的動靜整合交由了apache完成了,咱們能夠把靜態文件直接放置 在Apache這裏,若是這個靜態web服務器上升到CDN,那麼這些靜態資源就能夠在靠近用戶的地方使用,SSI說白了就是像apache這樣的靜態資 源服務器接收到服務端返回後,將一部份內容插入到頁面了,而後將完整頁面返回至瀏覽器。這個作法若是優化的得當,能夠很好的提高網站的加載效率。

  Apache這樣的靜態資源服務器還支持一種動靜整合的技術,這個技術就是ESi,它的英文全稱叫作Edge Side Includes,它和SSI功能相似,它的用法以下所示:

1
<esi:include src="test.vm.esi?id=100" max-age="45"/>

  它和SSI區別,使用esi標籤獲取的資源來自於緩存服務器,它和SSI相比有明顯的性能優點,其實網頁特別是一個複雜的網頁咱們作了動靜分離後靜態 的資源自己還能夠拆分,有的部分緩存的時間會長點,有點會短點,其實網頁裏某些動態內容自己在必定時間裏有些資源也是不會發生變化的,那麼這些內容咱們可 以將其存入到緩存服務器上,這些緩存服務器能夠根據頁esi傳來的命令將各個不一樣的緩存內容整合在一塊兒,由此咱們能夠發現使用esi咱們會享受以下優勢:

  優勢一:靜態資源會存放在緩存裏,那麼獲取靜態資源的效率會更高。

  優勢二:根據靜態資源的時效性,咱們能夠對不一樣的靜態資源設置不一樣的緩存策略,這就增長了動靜分離方案的靈活性。

  優勢三:緩存的文件的合併交由緩存服務器完成,這樣就減小了web服務器自己抓取文件的開銷,從而達到提高web服務器的併發處理能力,從而達到提高網站訪問效率的目的。

  (友情提示:ESI這塊我還了解的不太深刻,據說它其實能夠直接使用在jboss上,相關知識我還要繼續收集資料學習)

  SSI和ESI是靜態web服務器處理動靜資源整合的手段,那麼咱們再把動靜整合操做往前移,這個時候就到了瀏覽器端了。瀏覽器端的動靜整合的技術稱之爲CSI,英文全稱叫作Client Side Includes,這個技術就是時下javascriptMVC、 MVVM以及MVP技術採起的手段,實現CSI通常是採用異步請求的方式進行,在ajax技術還沒出現的年代咱們通常採起iframe的方式,不過使用 CSI技術頁面加載就會被人爲分紅兩次,一次是加載靜態資源,等靜態資源加載完畢,啓動異步請求加載動態資源,這麼一作的確會發生有朋友提到的一種加載延 遲的問題,這個延遲咱們能夠使用適當的策略來解決的,關於CSI的使用是本系列的重點,我會在後面文章裏重點講解。

 

網站靜態化處理—動靜分離策略(3)

前文裏我講到了網站靜態化的關鍵點是動靜分離,動靜分離是讓動態網站裏的動態網頁根據必定規則把不變的資源和常常變的資源區分開來,動靜資源作好了拆分之後,咱們就能夠根據靜態資源的特色將其作緩存操做,這就是網站靜態化處理的核心思路。因而可知,網站靜態化處理的核心就是動靜分離和緩存兩大方面,上篇我簡單講述了動靜整合的基礎知識,本篇將會講述兩大核心之一的動靜分離策略,只有把動靜分離策略作好了,緩存才能發揮出它應有的效果。

 

  下面咱們要討論下動靜分離的策略了,一個頁面什麼內容是動態的,什麼內容是靜態的,這個咱們到底該如何來區分了?這個問題學問很是大,咱們的標準不 同,最後拆分出來的動靜資源就會存在很大的不一樣。在本系列開篇裏,我提到了什麼樣的頁面是靜態頁面,什麼樣的頁面是動態頁面,我是以一個url的角度定義 的,每一個獨立的頁面都會有一個url,這個url就比如這個頁面的門牌號,咱們每次訪問這個url時候若是獲得的響應頁面都是同樣的那麼咱們就認爲該頁面 是靜態頁面,若是訪問某個url,咱們訪問的時間不一樣,最後展現的頁面也不同那麼這個頁面就是動態頁面,動態頁面就是咱們要進行動靜分離的載體了,咱們 能夠看到個人定義實際上是和時間相關的,也就是說訪問時間不一樣,獲得的結果會不一致,因此咱們能夠根據時間這個維度分析頁面裏那些內容是靜態的,那些是動 態,可是這個劃分在實際狀況裏就會變得很是複雜,下面我就講講這個複雜度。

  場景一:假如咱們是一個商戶,咱們查詢本身網店的交易數據,通常這個交易數據咱們會放置在頁面的右下部分,這個部分咱們很天然把它當作動態資源,就算咱們的網店交易量很小,咱們也不敢把這個部分當作靜態資源處理。

  場景二:咱們網站爲了給用戶一個友好的體驗,會在用戶登陸網站後在頁面某個地方顯示歡迎語,例如:上午好,夏天 的森林,歡迎使用咱們的網站!,到了下午,這個歡迎語可能就變成了下午好,夏天的森林,歡迎使用咱們的網站!,那麼這塊內容咱們應該是當作靜態內容仍是動 態內容呢?這個就須要思考了。

  場景三網站頁 面裏會有不少圖片,有些圖片的確是好久好久都不會發生變化,例如網站的圖標,可是有的圖片卻不一樣了,例若有一個星期咱們要爲某個商戶作營銷活動,那麼營銷 圖片這塊更新後就會有一個星期的有效期,複雜點的話,咱們可能會在營銷活動期間在頁面的某一塊專設給這個商戶營銷活動的內容區,這個內容區使用一個 html片斷,可是當營銷活動結束了,這個營銷的圖片可能就要發生變化,營銷的內容區可能會被去掉,那麼這些東西咱們是當作靜態內容仍是動態內容處理了?

  由上面的場景咱們能夠知道,這個動靜分離是要講究策略的,若是策略設計的很差,可能咱們把網站靜態化處理後,效果並無達到咱們的預期。其實,我認爲 動靜分離除了以時間維度考慮外,還應該有個維度,就是被拆分的資源是否須要服務端應用加以配合,例如像交易查詢這樣的動態內容,咱們其實須要服務端程序按 照必定的業務邏輯處理請求後從存儲層 獲取數據,那麼這種動態資源是無法作靜態化處理的,還有一部分資源例如場景三裏的圖片以及營銷的html片斷,這些資源寫好後在有效時間內是不會發生變化 的,那麼這塊內容雖然時效性可能會有差別,可是它倒是能夠在這段時間作靜態化處理的,還有種情形就是場景二了,這個場景雖然使用數據須要服務端參入計算, 可是計算結果在必定時間範圍內是不變的,也就是說結果是能夠被緩存的,那麼這塊的資源也是能夠當作靜態化資源進行處理的,爲何說拆分策略要考慮服務端應 用的因素了?由於上面這些場景都是由服務端應用參入的形式所決定,在有效時間裏服務端應用不須要參入,或者參入一次後,能夠長期保存結果,那麼咱們能夠把 這些資源當作靜態資源處理。

  除此以外,服務端應用和結果的密切度也是要當作考慮的因素的。在web開發裏,除了須要瀏覽器處理的,其餘技術均可以當作服務端來理解,若是咱們網站使 用到了CDN,使用到了靜態web服務器例如apache,以及服務端的web容器例如jboss,那麼按請求的行進路徑,咱們結果處理越早那麼網站響應 效率也就越高,因此當請求在CDN返回了,那麼確定比在apache返回效率高,在apache就返回了確定比jboss返回的效率高,再則服務端的 web容器自己由於服務端程序運行要消耗部分系統資源,因此它在處理請求的效率會比CDN和apache差不少,因此當咱們按照動靜分離策略拆分出了靜態 資源後,這個資源能不放在最底層的服務端的web容器處理就不要放在服務端的web容器裏處理。

  由上所述,咱們再回過頭來看看靜態web服務器的SSI技術,這個技術使用起來和咱們在服務端使用include相似,可是在SSI使用include必定會比在服務端效率高,由於服務端在整合動靜資源時候還會摻雜不少服務端程序處理,所以動靜資源的效率就會大打折扣。咱們再看看SSI的include的用法,以下所示:

<!--#include file="info.htm"-->

  這個寫法是使用頁面的註釋標籤, 當靜態web容器處理請求時候,它會掃描裏面的SSI標籤,接着就會處理這個標籤的內容,若是找到了資源那麼web容器會將資源插入到頁面裏,若是web 沒有處理這個SSI標籤,那麼等結果到了瀏覽器,這個也就是一個註釋而已,不會影響頁面的展現,並且SSI標籤處理的資源也是很是豐富的,無論這個資源是 靜態的,仍是動態的,只要獲取時候是個完整的資源都能被正常加入到頁面裏,因此像前面的場景二這種動態的內容也是能夠正常處理的。所以場景二,場景三這樣 的狀況均可以使用SSI來解決。SSI的做用固然不只僅只是能夠作include操做,它的標籤也能夠作一些邏輯上的操做,講述如何使用SSI不是本文的 重點,有興趣的朋友能夠去研究下。

  不過SSI也有本身的侷限性,它的第一個侷限就是SSI解析是靜態web容器來完成,所以它會消耗web容器的性能,若是SSI使用時候還有必定的邏輯,那麼這種性能消耗就會更大,其實我以爲更加劇要的是若是靜態web容器過渡使用SSI,那麼就會把本身變成了一個服務端的web容器,除了會影響到請求處理的效率,它還會下降自身的併發處理能力,因此咱們但願資源整合策略交給外部服務處理效果會更好些,如是有些大型互聯網公司使用ESI技術,ESI技術和緩存關係密切,這個內容我就放到下篇討論了。

  本篇最後我要再講講CDN的問題,上篇我講到靜態web容器整合動靜資源的好處,由此我說若是CDN能夠作動靜整合,那麼就能作到就近處理,這樣效果 會更高,今天我對這個作法作了一些考證,以爲該說法有點不妥,至少我如今的公司沒有使用到這樣的技術,CDN技術應該由三個步驟組成,首先是解析DNS, 找到離用戶最近的CDN服務器,接下來CDN要作一下負載均衡, 根據負載均衡策略將請求落地到最合適的一個服務器上,若是CDN服務器上就有用戶所須要的靜態資源,那麼這個資源就會直接返回給瀏覽器,若是沒有CDN服 務器會請求遠端的服務器,拉取資源再把資源返回給瀏覽器,如此同時拉取的資源也被緩存在CDN服務器上,下次訪問就不須要在請求遠端的服務器了,CDN存儲資 源的方式使用的是緩存,這個緩存的載體是和apache,nginx相似的服務器,咱們通常稱之爲http加速器,之因此成爲http加速器是爲了和傳統 靜態web服務器區別開來,傳統的靜態資源服務器通常都是從持久化設備上讀取文件,而http加速器則是從內存裏讀取,不過具體存儲的計算模型會根據硬件 特色作優化使得資源讀取的效率更高,常見的http加速器有varnish,squid。Ngnix加上緩存模塊也是能夠當作http加速器使用的,無論 使用什麼技術CDN的服務器基本都是作一個就近的緩存操做,這也就是說CDN是否能夠完成SSI操做是值得商榷的,因此前文的說法仍是有點問題的。

來源:夏天的森林

 

網站靜態化處理—緩存(4)

 

上篇我補充了下SSI的知識,SSI是一個十分常見的技術,記得多年前我看到不少門戶網站頁面的後綴是.shtml,那麼這就說明不少門戶網站都曾經使用過SSI技術,其實如今搜狐網站也還在用shtml,以下圖所示:

 

 

  因而可知SSI在互聯網的應用仍是很是普遍的。其實互聯網不少網頁若是咱們按照動靜分離策略拆分,絕大部分都是能夠當作靜態資源處理,例如新聞網站, 文學網站,這些網頁生成後,大部分的資源都是不變的,說白了這些網頁本質就是一個靜態頁面,咱們開發他們時候也不須要服務端的參入,每個網站都有本身固 定的板式,假如每一個新網頁都要完完整整的開發,重複性的工做實在太多了,出錯的機率也很是高,在本系列第二篇裏我曾經詳細介紹了velocity的佈局模 板技術,其實SSI也能夠製做出一套固定的模板,開發時候咱們只須要在定義好的模板裏添加或者修改咱們須要更改的內容就能夠完成一個頁面的製做,可見 SSI技術爲了咱們開發網頁提供了很大的便利。

 

  與SSI相對應的還有ESI,這個技術不是太經常使用,在網上能收集到的資料也不是太多,網上有限的資料也基本都是和淘寶相關的,不過仔細研究下淘寶對ESI的運用,對於理解網站靜態化處理是很是有借鑑意義的,下面我將重點講講ESI的知識。首先看個場景啊。

 

  咱們登陸支付寶網站,到了我的首頁咱們發如今支付寶下面有一個條目,以下圖所示:

 

 

  這是支付寶默認給咱們顯示的【生活好助手】,右邊有個箭頭,咱們點開它,以下圖所示:

 

 

  咱們看到這裏有添加的按鈕,經過添加按鈕,咱們能夠添加其餘經常使用的組件圖標。(注意:我這裏只是以支付寶這個功能爲例,支付寶是否按照我說的設計,這個我就不清楚了)

 

  若是咱們按照本身個性化添加了本身的組件,不一樣的人添加的經常使用組件也會是不盡相同的,若是咱們本身開發的網站也有這樣的功能,那麼咱們該如何設計了? 咱們通常都會很直觀的把這個新增的組件信息存儲在數據庫裏,用戶每次登陸時候該信息也會隨之從數據庫裏讀取,可是這個場景對於像支付寶這種用戶量極大,日 均訪問量極高的大型網站,這種個性化又非核心的功能都從數據庫裏讀取,那麼它對數據庫形成的壓力必定是十分巨大的,在存儲的瓶頸裏咱們講了那麼多優化數據 庫的手段,其核心手段有一個就是減小對數據庫價值不高的操做,而這種個性化配置跟支付寶的支付操做相比起來,價值度實在過低,所以咱們最好的選擇是避免數 據庫承擔過多此類的操做。

 

  不過像上面這個場景裏的功能,它所使用的數據又不是那種無關緊要的,假如數據存儲的不可靠形成數據丟失仍是會形成沒必要要的麻煩,因此咱們仍是會把這些 信息作持久化存儲。此外像上面的【生活好助手】條目仍是頁面的一個重要組成部分,所以像SSI那種使用html註釋指令,當指令沒法正常解析,就直接返回 到瀏覽器,由於是註釋,因此頁面也不會顯示它,SSI的這種作法用在上面場景確定是不太合適的。這樣的場景在電商網站裏是十分常見的,例如一個商品頁面, 頁面裏會有商品的圖片,還有商品的詳細介紹,這些內容其實都是會用持久化系統進行存儲,同時它們自己也是網頁的重要作成部分,若是碰到問題就忽略最終會造 成頁面顯示錯誤。

 

  結合上面的場景咱們來討論下ESI技術了,ESI技術和SSI技術相似,其實也和jsp裏的include指令相似,它也是在頁面裏使用一個指令標籤 web容器解析這個標籤後將獲取的數據替換掉這個標籤。咱們來看看ESI的使用方法,咱們能夠在velocity裏自定義一個esi標 籤,velocity裏的使用以下所示:

 

esiTool.setTemplate('test.vm').addQueryData('id', 100)

 

  velocity引擎解析vm模板,最終會把vm解析成html頁面,這個時候該頁面裏使用esi標籤的地方就被轉化爲:

 

<esi:include src="test.vm.esi?id=100" />

 

  當頁面到了服務端web容器以前的靜態web容器(該web容器要安裝好解析esi的模塊),靜態web容器就會解析這個esi標籤,靜態web容器 會以test.vm.esi?id=100 做爲key,到緩存系統裏查找信息,若是查到了信息,緩存服務器就直接返回,用返回內容替換掉esi標籤,若是緩存裏沒有找到則會直接請求持久化系統,持 久化系統返回信息後,緩存系統將信息緩存起來,同時也將信息返回至靜態web容器,那麼下次用戶再訪問一樣內容就會直接從緩存裏讀取了。

 

  ESI技術和SSI很像,只不過ESI技術是和緩存技術配合起來的,同時ESI標籤也不像SSI標籤那樣使用註釋的形式,所以ESI標籤是必定要被解 析的,若是僅僅是緩存,ESI和SSI比較起來也沒顯得那麼有優點和特別,可是對於電商這種場景而言ESI的現實意義很是大,電商網站也是一個由用戶產生 內容的網站,每個商家的店鋪雖然咱們都知道它是屬於淘寶或天貓的,可是單獨一個商家的店鋪都是個性化很強的,與其餘店鋪差別很大,爲了買賣商品,商家會 上傳本身商品的圖片,還會使用圖片和文字描述本身的商品,單個商品頁面咱們作動靜分離分析,很容易分辨出動態內容和靜態內容,可是若是一個電商平臺擁有超 乎想象數量的商家,那麼每一個頁面的圖片,文字和商家頁面的關係就會變得有點微妙了。因爲電商網站的圖片特別多,那麼電商網站系統通常都會設計一套管理這些 小圖片的分佈式存儲系統,例如淘寶的TFS文件系統,它是專門針對圖片使用的分佈式文件系統,這些文件系統裏存儲的圖片會和商家緊密關聯,這就讓圖片自己 擁有了必定的動態屬性,可是對於每一個商家頁面而言,商家本身的圖片資源都是能夠靜態化的,也就是說圖片的讀取是要經過商家信息進行計算的,計算出的結果對 於商家而言又是靜態的,能夠被緩存的。可是這個靜態資源的處理時候就變得複雜了,而這些是SSI沒法完成的。

 

  首先咱們直接從文件系統讀取圖片,效率是很是低效,所以咱們仍是會把它們緩存在內存裏,可是因爲不一樣圖片和不一樣商戶是相關聯,那麼對於緩存查找時候是 須要必定的條件,不一樣商戶對本身頁面的設計方案也會有所不一樣,通常商戶這些資源,存儲系統確定會按照設計模板的維度存儲,不一樣商家因爲商品和文化的不一 樣,那麼使用的模板也不同,所以資源返回靜態web容器前還須要一個整合過程,這樣場景下的靜態資源獲取實際上是須要必定邏輯計算的,那麼這個計算通常都 會在開發時候由代碼完成,因此從上面ESI使用的例子看到,開發人員會使用velocity的esi標籤,這個標籤開發人員能夠設置參 數,velocity引擎最終會將這個標籤解析成靜態web容器能夠解析的esi標籤,標籤裏有這樣的代碼test.vm.esi?id=100,文件後 面會帶上參數,那麼這個參數實際上是動態的,那麼這個參數也就是緩存系統獲取正確信息的規則了。這樣咱們就完成了靜態資源獲取的邏輯計算,計算完畢後這些資 源會在一段時間裏長期有效,所以它就演變爲靜態資源,能夠被緩存了。ESI比SSI強大多了,同時ESI也能夠完成SSI的功能,因此使用了ESI也就沒 必要用SSI了。

 

  像咱們平時作web開發時候可能沒有太留心一個問題,通常的web開發裏使用的靜態資源例如圖片,css文件,js文件咱們都會放置在一個 resource包裏,若是是企業開發,這個web應用上線時候也就直接打包在web工程裏,一些互聯網網站也只不過會將這些資源放置在單獨的靜態服務器 上,我平時開發時常聽到有人說,項目裏圖片太多了,應該合併下,css文件和js文件也太多了也要合併下,這個多到底多多少了,幾十個文件,幾百個文件, 這個要和社交網站,電商網站這種用戶能夠產生圖片的網站比起來那就是小巫見大巫了,由於用戶能產生內容的網站靜態資源會隨着時間推移文件規模變得異乎尋常 的大,因此此類網站的靜態資源已經無法放置在項目下,它就要求咱們須要有新的手段管理這些靜態資源,而且有新的手段使用這些靜態資源,那麼像TFS文件存 儲系統出現了,緩存技術出現了,最後咱們在應用裏使用ESI技術把它們整合到咱們網頁裏,經過這個分析咱們就能明白ESI適用的業務場景了。

 

  網站靜態化處理咱們首先要按規則拆分動靜資源,拆分出來的靜態資源該如何處理就是網站靜態化處理的關鍵所在,把靜態資源處理從服務端的web應用裏剝 離出來,不讓服務端的web應用參入過多的靜態資源解析,這樣就能夠爲服務端的web應用減小沒必要要的處理操做,從而達到提高服務端web應用的運行效 率,接着咱們就把拆分出來的靜態資源處理操做往前推移到靜態web服務器,前兩篇文章和今天的文章我着重講解了靜態web服務器處理靜態資源的手段,那麼 這裏有個問題了,這些處理能夠再往前推到瀏覽器來完成嗎?答案固然是否認的,首先瀏覽器的緩存是很是不可靠的,若是用戶把瀏覽器設置爲不緩存任何數據的模 式,那麼瀏覽器就無法緩存數據了,而用戶的行爲那是根本無法控制的,其次瀏覽器緩存的數據量是有限的,若是咱們要在瀏覽器進行緩存也是緩存最有價值緩存的 數據,更重要的一點,爲了作好網站靜態化處理咱們對網頁的動靜資源作了拆分,可是拆分出的靜態資源也並非徹底不須要進行任何邏輯處理就能使用的,例如前 面講到的ESI適用的場景咱們就發現,有些靜態資源的獲取仍是要不少條件的參入,而這些條件是由動態數據產生的,那麼這樣的靜態數據瀏覽器是無法作緩存 的,這點也說明了拆分出來的靜態化資源絕大部分仍是要停留在服務端的,竟然只能停留在服務端,那麼最爲高效的處理這些靜態資源的地方就是CDN和靜態 web容器了。因此在本系列的第一篇裏我講到網站生產部署時候最好是在服務端web應用以前放置一個靜態web容器,若是有了這樣的靜態web容器作反向 代理,那麼咱們就可讓它來完成靜態資源的相關操做,並且靜態web容器還能輔助完成一些邏輯上的處理,從而彌補了CDN的不足之處。固然這麼作的好處不 僅僅只有這些,第二篇文章裏我曾經討論了反向代理的好處,可能你們印象還不是很深入,我將會在後面文章裏對反向代理作更加深刻的分析。

 

  講完了ESI的妙用後,我下面將講講本篇的主題緩存了。其實單獨講緩存真的沒啥太多內容,不過在網站靜態化處理裏的緩存仍是和存儲裏講到的分佈式緩存 有所不一樣,分佈式緩存的數據都是存儲在內存裏,而網站靜態處理的緩存既有存儲在內存裏還有存儲在硬盤上,固然存儲在內存裏讀取速度會更快,可是網站的讀取 效率還和資源距離用戶的遠近有關係,例如瀏覽器的緩存實際上是把靜態內容緩存在硬盤上,可是由於不須要經過網絡獲取資源,所以它的讀取效率就顯得特別高了, 除了這個因素外還有個因素,咱們前面作動靜拆分的目的就是想拆分出靜態資源,讓這些靜態資源遠離服務端的web應用,這樣能夠減小服務端沒必要要的壓力,從 而達到提高服務端web應用處理能力,而把靜態資源放置在靜態web容器處理,它的處理靜態資源效率又會高於在服務端web應用的處理,從而也達到提高靜 態資源讀取的效率。不過若是靜態資源最終只能放置在服務端,那麼這個時候咱們把靜態資源存入到緩存裏即內存裏效率確定比在硬盤上高,因此CDN的服務節點 通常都是採起將靜態資源存儲在內存裏,就算是服務端web應用前的靜態web容器,若是咱們讓靜態資源緩存在內存裏,效率確定也是比在硬盤上高。

 

 

網站靜態化處理—CSI(5)

講完了SSI,ESI,下面就要講講CSI了 ,CSI是瀏覽器端的動靜整合方案,當我文章發表後有朋友就問我,CSI技術是否是就是經過ajax來加載數據啊,我當時的回答只是說你的理解有點片面,那麼到底什麼是CSI技術了?這個其實要和動靜資源整合的角度來定義。

  CSI技術實際上是在頁面進行動靜分離後,將頁面加載分爲兩個步驟完成,第一步是加載靜態資源,靜態資源加載完畢後進行第二步驟加載動態資源。不 過這個定義仍是表述的不全面,不全面的地方就是咱們要強調動靜分離的目的,咱們把頁面裏的動靜資源拆分出來是爲了將靜態資源作有效的緩存,這個靜態資源可 能是在靜態web容器上,也有多是在CDN上,也有多是在瀏覽器上,無論靜態資源是如何緩存的,咱們的目的都是爲了讓靜態資源加載的速度更快,若是我 們沒有讓靜態資源加載變得高效,就算咱們使用了CSI的形式來設計頁面,其實也沒有發揮CSI的優勢,反倒還會一不當心引入CSI的缺點。那什麼是CSI 的缺點呢?具體以下:

  CSI的缺點一:CSI不利於頁面的SEO即搜索引擎優化。搜索引擎的網絡爬蟲通常是根據url訪問頁面,獲取 頁面的內容後去掉沒用的信息例如:css樣式,js腳本,而後分析剩下的文本內容,所以假如頁面的一部份內容須要進行異步加載,那麼這個加載控制確定是由 javascript代碼來完成的,所以網絡爬蟲爬下來的頁面裏異步加載的操做是無法執行的(據說有些高級的爬蟲能夠執行異步的操做,抓取異步的內容,即 便有這個技術,大部分主流的爬蟲仍是會忽略掉javascript代碼的也會忽略異步加載的內容的),這就會致使爬蟲爬的頁面裏有部分信息丟失了,因此說 CSI對SEO不太友好。不過這個缺點咱們仔細分析下,可能並不會是那麼嚴重,前面咱們談論了不少靜態分離的策略,若是咱們動靜分離策略作的好,那麼動態 資源基本都是不能被緩存的內容,常常發生變化的內容,這些變化的內容原本就不須要被網絡爬蟲爬到,就算真的被爬到,搜索引擎有個查詢結果指向了這個頁面, 咱們點開這個頁面結果也是在頁面找不到被搜索的關鍵字,這種情形我相信不少朋友在使用搜索引擎時候都會碰到過。不過我想若是開發人員沒有正確使用CSI, 那麼這塊他們可能也不會處理的特別好,所以這個缺點仍是很容易被引入的。

  CSI的缺點二:咱們那麼費時費力想讓本身的網站靜態化,目的就是想讓頁面加載更快點,咱們簡簡單單把頁面加載 分紅了兩個步驟進行,那麼這麼作就真的快嗎?這可不必定啊,其實動靜分離的作法和我上一個系列裏講到的數據庫讀寫分離有相似之處,數據庫讀寫分離咱們是通 過拆分原表的讀寫之間的關聯關係,從而達到解決讀的瓶頸問題,而網頁的動靜分離是由於靜態資源很容易被優化,因此咱們要拆分動靜資源。因此當咱們對資源進 行了動靜分離,可是又沒有優化靜態資源,這個一看就知道咱們缺乏一個加速頁面加載速度的操做,那麼真的能讓頁面加載快點,還真的很難說了,並且異步加載需 要執行javascript代碼才行,可是靜態資源加載時候很容易形成javascript腳本被阻塞,若是阻塞的腳本正好是異步加載的部分,結果只會是 比之前加載的更慢了。

  因而可知,我在前面講到的SSI和ESI技術對於咱們在瀏覽器端發揮CSI技術優勢是很是有必要的,SSI和ESI作好了能讓動靜分離出的靜態資源加 載的更加高效,這也就讓CSI操做的第一個步驟變得高效,第一個步驟處理好了咱們只要在頁面控制好腳本阻塞對異步加載的影響,那麼咱們就能夠達到提高整個 頁面加載效率的目的了。此外我以爲CSI對SEO有重大影響是個僞命題,假如使用CSI形成了SEO效果不佳,那麼確定是咱們CSI方案設計的不到位。

  有人認爲CSI還會有個缺點,不過筆者我並不認爲這是一個缺點,這實際上是一個設計問題,好與壞是根據我的的操做習慣所決定的。這個別人認爲的缺點是什 麼呢?它就是使用CSI技術時候,雖然頁面很快的被加載出來了,可是動態內容那部分可能會顯示一個正在加載的提示,那麼這就致使頁面用戶友好性下降,其實 這種同步和異步加載混搭操做實在太常見了,幾乎全部大型門戶網站,電商網站還有一大堆數不盡的網站都是採用同步和異步混搭的加載方式,假如這些網站不這麼 作,我相信這些網站例如首頁加載必定會慢的讓人吐血,由於它們不少網頁裏面內容實在太多,圖片也都有點爆棚了,因此它們不得不使用同步和異步混搭的加載方 式,甚至不少靜態資源例如圖片,flash這些東西也會採起異步加載方式。說到這裏,估計有人仍是以爲不服氣,他就是不喜歡頁面加載時候還要出現個正在加 載提示,可是網頁裏又很是須要CSI帶來的好處,那麼咱們該如何解決這個問題呢?這個問題很好解決,首先願意使用CSI技術也就說明用戶仍是很願意使用異 步的加載技術的,不喜歡則是正在加載的提示,這說明用戶想要在作同步加載操做時候不要摻雜異步操做,雖然如今ajax技術大行其道,可是ajax技術有個 同步加載是沒有辦法解決的,那就是咱們在瀏覽器地址欄裏輸入網站url請求頁面 ,因此面對上面的需求咱們只要保證這種同步操做只是一個純粹的同步操做而不要摻雜異步加載便可,這個方案仍是很好實施的,這裏我就再也不累述了。

  動靜分離後咱們會把靜態資源進行緩存,前面文章裏講了一大堆都是在講服務端的靜態資源緩存,如今講到了CSI已經到了瀏覽器端,那麼咱們就得談談瀏覽 器的緩存操做。頁面的緩存操做就是使用http的expires和cache-control,咱們首先看看下面的寫法:

<meta http-equiv="pragma" content="no-cache">

<meta http-equiv="cache-control" content="no-cache">

<meta http-equiv="expires" content="0">

  這是我如今作的java的web項目裏,jsp和vm文件都會使用的meta配置,它的目的就是讓頁面不要被瀏覽器緩存,可是若是使用CSI技術,同 時動靜分離作的很好,那麼在頁面頭部其實咱們能夠再也不這麼寫了,咱們可讓頁面在合理的時間範圍內被瀏覽器緩存,若是該頁面作了緩存操做,那麼之後咱們再 訪問該頁面,網頁的加載效率就會變得更高了。

  這裏還有個問題,在雅虎優化網站的建議裏,爲了充分利用網頁並行加載的特色,咱們每每會把圖片,外部的js和css文件放置在單獨的靜態web容器或 CDN上,那麼這些文件每每也是能夠被瀏覽器緩存,這個咱們又如何設置才能讓瀏覽器知道要緩存它們呢?這裏咱們以apache爲例,爲了讓靜態資源被瀏覽 器緩存,apache須要使用mod_expires模塊,而後在apache的配置文件裏添加以下配置:

<FilesMatch "\.(gif|jpg|png|js|css">ExpiresDefault "access plus 10 years"</FilesMatch>

  那麼瀏覽器訪問此apache上的靜態資源後,瀏覽器就會把圖片和該服務器上的js和css文件緩存在瀏覽器裏。

  咱們看看被緩存的靜態資源是如何被使用的,以下圖所示:

  當http的響應碼是304的時候,那麼瀏覽器就會從緩存裏讀取資源了,這裏有的朋友可能會感到奇怪爲何緩存的資源還要發送個http請求了?理解 這個咱們就要了解下緩存的機制,緩存的含義是臨時保存某些東西,既然是臨時保存,那麼就應該有個保存的有效期,咱們定義緩存的方式是經過http完成的, 那麼按道理檢查緩存是否過時也應該是http來決定的,所以每次使用緩存時候咱們要發個請求到服務端,服務端會檢查下資源是否過時了,若是沒有過時,服務 端返回個304的響應碼,304的返回響應是沒有http報文體的,因此這個http請求的返回數據是很是小的,所以這個http效率仍是很高的,若是服 務端發現資源過時了那麼服務端就會把新資源返回給瀏覽器了,其實這個檢測資源是否過時的請求有個專有名詞叫作條件Get請求。 至於服務端是如何完成檢查操做,本系列在講web前端優化時候會詳細闡述,這裏就不深刻了。看到這裏估計有朋友又有疑問了,爲何緩存是否過時不能在瀏覽 器端來作了?這主要是瀏覽器作這個檢查很是不許,由於用戶的電腦時鐘不必定準確,或者用戶電腦時鐘和服務端不一致,若是再加上時區那麼就更加麻煩了,因此 緩存失效最好是在服務端進行,這樣緩存的有效期的準確性才能獲得保證。html5的出現,瀏覽器緩存的能力大大加強了,不過使用html5技術進行緩存我 尚未深刻研究過,因此這裏也不講述了,有興趣的朋友能夠本身研究下。

  好了,CSI主題內容講完了,講到CSI技術和瀏覽器咱們就能夠開始本系列另外一個重要內容先後端分離了,這將是我下篇的主題,我在本身博客裏屢次講到先後端分離,立刻又要再次講了,此次講是我這麼長時間作先後端分離研究的大總結了。

網站靜態化處理—先後端分離—上(6)

前文講到了CSI技術,這就說明網站靜態化技術的講述已經推動到了瀏覽器端了即真正到了web前端的範疇了,而時下web前端技術的前沿之一就是前 後端分離技術了,那麼在這裏網站靜態化技術和先後端分離技術產生了交集,因此今天我將討論下先後端分離技術,先後端分離技術討論完後,下一篇文章我將會以 網站靜態化技術的角度回過頭來從新審視下先後端分離技術,但願經過這種審視來加深咱們對兩套技術的理解。

  先後端分離技術我我的認爲是web前端被專業化之後的必由之路,而nodejs的出現是先後端分離技術的一個強興的催化劑,緣由是nodejs的出現 削平了前端技術和服務端技術之間的鴻溝,使得先後端兩套不一樣技術體系進行真正意義的解耦提供了無限的可能性。可是若是咱們把nodejs技術的使用認爲就 是實現了先後端分離,這種理解又實在太膚淺了,下面我將講講我研究過的先後端分離技術方案,以及這些技術方案隱藏在背後思考,但願這些思考能給你們以一個 新的思路來理解先後端分離技術。

  咱們要深入理解先後端分離技術有一個重要的前提,那就是要把先後端分離技術認爲是傳統的web應用裏的MVC設計模式的進一步演進。那麼咱們首先來看看MVC的定義,下面的內容摘錄於維基百科的解釋,具體以下:

MVC模式(Model-View-Controller)是軟件工程中的一種軟件架構模式,把軟件系統分爲三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。

MVC模式最先由Trygve Reenskaug在1978年提出[1] ,是施樂帕羅奧多研究中心(Xerox PARC)在20世紀80年代爲程序語言Smalltalk發明的一種軟件設計模式。MVC模式的目的是實現一種動態的程式設計,使後續對程序的修改和擴展簡化,而且使程序某一部分的重複利用成爲可能。除此以外,此模式經過對複雜度的簡化,使程序結構更加直觀。軟件系統經過對自身基本部分分離的同時也賦予了各個基本部分應有的功能。專業人員能夠經過自身的專長分組:

(控制器 Controller)- 負責轉發請求,對請求進行處理。

(視圖 View) - 界面設計人員進行圖形界面設計。

(模型 Model) - 程序員編寫程序應有的功能(實現算法等等)、數據庫專家進行數據管理和數據庫設計(能夠實現具體的功能)。

  各種用於Web應用開發的語言裏都有屬於本身的MVC框架,例如本人最熟悉的服務端語言java裏就有大名鼎鼎的struts2,springMVC 的MVC應用框架,我早期從事java的web開發時候認爲這些MVC框架都是很是的博大精深,用途普遍,可是當我逐漸轉向了web前端技術開發之後又覺 得這些框架的不少功能顯得那麼的多餘和累贅,所以我曾寫過一篇文章專門討論過這些問題,該文章的名字叫作《爲何作java的web開發咱們會使用struts2,springMVC和spring這樣的框架?》。

  其實這篇文章被寫的源頭就是在於我認爲像struts2和springMVC這樣的框架作了太多瀏覽器自己就能夠完成的工做,例如:頁面的渲染操做, 由於服務端搶了瀏覽器端的部分工做,這其實也就等於限制了web前端技術的深刻運用,像不少前端的優化技術以及不少提高用戶體驗的技術就很難派上用場,之 因此產生這些問題,我認爲傳統的MVC框架本質實際上是一個服務端的MVC框架,雖然MVC設計模式裏的V即View視圖層是想把界面開發工做專業化,讓界面設計人員能專心於界面開發,可是傳統的MVC框架下的View層的本質倒是一個徹徹底底的服務端技術。

  咱們以java的web開發裏jsp爲例,JSP全名爲Java Server Pages,中文名叫java服務器頁面,其根本是一個簡化的Servlet設計,它是java裏動態網頁的技術標準, 這就說明jsp雖然看起來像html,其實它並非真正的html,它須要被java的web容器進行解析轉化爲瀏覽器能夠解析的html頁面,而後經過 網絡傳輸到瀏覽器後,瀏覽器才能正確的展現這個jsp頁面,其餘web開發語言裏都有相似的動態網頁技術標準,可是無論什麼語言的動態網頁技術標準,咱們 使用它時候就是讓web前端技術被服務端技術所綁架,這也就是爲何每一個招聘web前端工程師的崗位都要問你是否會java,php語言的源頭。可是隨着 互聯網的大發展,對web前端的要求是愈來愈專業化,web前端自己所包含的技術難度已經不亞於任何一個服務端語言開發難度,所以咱們須要web前端更高 的專業化,而不但願web前端工程師被服務端技術束縛的更多而限制了自身能力的發展,這就致使先後端分離技術的出現。

  不過先後端分離技術的第一階段倒不是從改變view層即視圖層開始的,而是從鏈接客戶端和服務端的C層即控制層開始的,控制層既要做用於客戶端又要做 用於服務端,若是一個功能頁面是一個程序員從瀏覽器端一直寫到模型層,控制層也就不是什麼問題了,可是若是當咱們想按MVC的設計思想,讓界面開發人員專 注於頁面開發,服務端開發人員專一於服務端開發,那麼這個時候控制層的歸屬問題就顯的很是重要了。在傳統的MVC框架裏,由於M層和C層是使用一樣的語言 體系,所以咱們很天然會把M層和C層的開發工做都交由服務端開發人員完成,這個決定無可厚非,可是傳統的MVC框架裏V層和C層其本質也是同一個技術體系 下的(例如java的web開發裏的jsp本質就是個servlet),所以V層和C層也是緊耦合的,所以界面開發人員開發頁面時候如何沒有C層支撐,那 麼這個頁面實際上是根本跑不起來的,若是前端開發人員這時候跑去寫寫C層即控制層的代碼,這就打破了原有的橫向分工,這個時候控制層的編碼工做就會變得混亂 而難以控制,看到這裏有人必定會說既然控制層是屬於服務端的,那麼前端技術人員就等等服務端的開發進度,再不行就本身寫個mock模擬下服務端的控制層, 聽到這種建議,我相信無論是前端的仍是服務端的技術人員都會頭腦發麻,第一反應就是這不是自找麻煩啊,還不如一我的所有搞定算了。由此第一階段的先後端分 離技術方案出現了,這個方案須要解決的問題就是如何能讓web前端技術人員和web服務端技術人員協同起來工做,合理的分工,換句話說就是按web前端和 web服務端角度如何能橫向的分解web的開發工做。

       先後端分離的第一階段須要解決問題的核心就是控制層的歸屬問題,從技術角度而言就是控制層究竟是應該和視圖層解耦比較合理仍是跟模型層解耦比較合理的問題。那麼咱們這裏先回顧下MVC設計模式裏對控制層的定義,維基百科裏的定義是:

(控制器 Controller)- 負責轉發請求,對請求進行處理。

  不過這個解釋我認爲並不全面,以java的web開發裏的控制層設計爲例,咱們發現控制層以溝通視圖層和模型層的角度而言,控制層其實主要完成三項具體的工做,它們分別是:

  工做一:控制層起到一個路由的做用。客戶端請求到達控制層後,控制層根據請求內容將請求路由到服務端某個模型層進行處理,模型層將請求處理完畢後,會把響應結果返回給控制層,控制層在根據響應信息路由到特定的頁面。

  工做二:控制層起到一個報文信息格式轉化的做用。這裏以java的web開發爲例,瀏覽器的數據都是以http報文形式發送給服務端,而控制層就是將http報文信息解析成java的對象,固然也能夠是java的基本數據類型,而後控制層把解析好的信息傳遞給模型層進行處理。

  工做三:傳統的MVC框架裏,控制層其實深刻參入到了頁面渲染的操做。在java的web開發裏的控制層無論如 何被包裝,其本質就是一個servlet,而jsp頁面本質也是個serlvet,所以咱們能夠這麼理解jsp,jsp就是以頁面開發的方式寫java, 而servlet就是以java的方式寫頁面,因此咱們能夠在servlet裏以文件流的方式輸出頁面,也可讓servlet跳轉到jsp頁面。

  由上面的論述裏咱們發現,其實傳統MVC框架裏控制層和模型層的聯繫方式相對很簡單的,它們的聯繫主要是路由和報文格式的轉化上,而控制層與視圖層的 聯繫除此以外還多了一個頁面渲染,而頁面渲染自己應該是屬於瀏覽器的技術範疇,是瀏覽器技術不可分割的一部分,也是我上面內容裏詬病傳統MVC框架問題所 在,若是控制層承擔了頁面渲染工做,那麼控制層和視圖層的耦合度就變得很是高,要想將其解耦是十分困難,通常只有咱們打破了現有MVC框架的技術體系才能 完成,相比之下,控制層與模型層的解耦就顯得容易多了。那麼控制層與模型層如何解耦呢?具體以下:

  首先咱們來解決下報文格式轉化的問題,這個技術方案很簡單就是借鑑http統一報文格式的特色,咱們爲控制層和模型層定義一套統一的報文格式,例如我 們定義控制層和模型層都以map的數據類型進行數據傳遞,這個map裏有個專門的字段用來定義被路由到的模型接口信息,有個字段專門存儲須要傳遞的數據, 具體的設計方案能夠根據實際的業務須要來設計。

  接下來就是路由的問題了,在解決報文格式轉化問題的論述裏我講到要在統一報文格式裏專門定義一個字段用來存儲該數據到底路由到哪一個模型進行處理,不過 這個字段並不能徹底解決路由問題,所以咱們須要模型層對控制層提供一個統一的接口,任何控制層與模型層的溝通都經過這個統一接口來完成,只不過不一樣請求報 文組裝的內容不同而已,而這個接口還有個重要職責就是解析報文裏的路由信息,讓請求能被正確的路由到對應的模型接口所處理。固然這個接口的返回值最好也 是一個統一的報文格式,這樣控制層解析模型層的返回數據也會便利的多了。

  由上所述,咱們發現第一階段的先後端分離工做控制層應該歸屬於web前端,這麼作更加合理,也更加容易實現,其實以後進化版的先後端分離方案,控制層也都是屬於web前端,只不過形式不一樣而已,這個我在下一篇文章裏繼續討論。

  第一階段先後端分離方案解決的核心就是讓控制層和模型層解耦,這個方案進一步演化一下,咱們能夠把控制層和視圖層獨立成一個web應用,模型層也獨立成一個web應用,兩個web應用之間經過遠程調用方式進行溝通,這個方案我在之前文章裏寫過,這篇文章的名字叫作《我設計的網站的分佈式架構》。

  這個進化版的方案增長了系統開發的難度,由於咱們須要增長網絡通訊的編程以及遠程調用的實現,更麻煩的是咱們還須要進行復雜的多線程編程,既然增長了 開發的難度爲何我還要這麼作呢?首先咱們經過應用分層,能夠動態的調節web前端和web服務端的負載壓力,還能夠在模型層以前提供一道安全屏障,不過 被服務端綁架的web前端在提高整個web應用負載能力這塊仍是頗有限的,其實這種作法的最大好處就是利於SOA框架的設計,也就是說這種架構咱們能夠爲 服務端的SOA化提供有力的保障,由於控制層和模型層的解耦,可讓模型層真正作到專一於業務,而不會再發生那種把業務邏輯寫到控制層的問題了從而下降代 碼的健壯性。

 

網站靜態化處理—先後端分離—中(7)

上篇裏我講到了一種先後端分離方案,這套方案放到服務端開發人員面前比放在web前端開發人員面前或許獲得的掌聲會更多,我想不少資深前端工程師看 到這樣的技術方案可能會有種說不出來的矛盾心情,當個人工做逐漸走向愈來愈專業化的前端開發後,我就時常被這套先後端分離方案所困惑,最近我終於明白了這 個困惑的本源在哪裏了,那就是這套先後端分離方案實際上是服務端驅動的先後端分離方案,它的實現手段又是從服務端的 MVC架構體系演化而來,所以該方案最大的問題就是它並無從根本上改變web前端從屬於服務端的被動局面。那麼問題來了,有沒有以web前端爲驅動的前 後端分離方案呢,該方案能讓web前端的能力得到更大的釋放了?答案是絕對有。本篇就要講講以web前端驅動的先後端分離方案。

  首先要提的就是javascriptMVC,下面我摘抄的是維基百科裏對javascriptMVC的解釋,具體以下:

  首先是簡介:

JavaScriptMVC 是一套開放源代碼的多樣化互聯網應用程序框架,以 jQuery 與 OpenAJAX 爲基礎。JavaScriptMVC 利用 MVC 架構與工具擴展這些函式庫,以便開發與測試。因爲 JavaScriptMVC 不須要任何服務器端的配合,所以它能夠和任何的網站服務接口與編程語言整合,如 ASP.NET、Java、Perl、PHP、Python 或 Ruby。

  接下來是歷史:

JavaScriptMVC 的第一個版本是在2008年5月釋出。穩定版的 JavaScriptMVC 2.0 在2009年6月釋出,並以 jQuery 爲基礎。主要開發目標爲維持程式碼的簡短和專一在它獨特的功能上。3.0版本在2010年12月釋出。而從 JavaScriptMVC 中所獨立出來的 MVC 架構「CanJS」則在2012年4月釋出。

  從維基百科裏的解釋咱們會發現以下啓示,它們分別以下:

  啓示一:javascriptMVC是一個應用框架的名字,這和jQuery的命名是同樣的,因此這裏我要聲明 一下,本系列裏的javascriptMVC不是指代這個框架,而是指代的是使用javascript語言實現出的一類的web前端的MVC框架,本系列 後面的javascriptMVC和前端MVC的含義是一致的。

  啓示二:從javascriptMVC歷史裏咱們能夠看到初版的javascriptMVC產生於2008年,這個歷史要遠早於nodejs出現的時間,這說明了前端的MVC並非由於nodejs的出現而產生的,應該是nodejs推進了前端的MVC框架的應用和普及。

  啓示三:維基百科裏有一段解釋:

因爲 JavaScriptMVC 不須要任何服務器端的配合,所以它能夠和任何的網站服務接口與編程語言整合,如 ASP.NET、Java、Perl、PHP、Python 或 Ruby。

  這段話說明了前端MVC的一個很重要的特色就是前端MVC能夠擺脫服務端語言的束縛作到真正的獨立,同時前端MVC又能夠和任何服務端語言進行整合, 你們能夠試想下若是咱們開發的web應用前端達到了前端MVC的程度,那麼公司在招聘web前端工程師的時候就不在會問你「你會java嗎?」或者「你會 php嗎?」假如這個前端工程師所會的服務端語言能力和公司不匹配,面試官也不會再猶豫和搖頭了。

  啓示三同時還隱含了一個問題,爲何好的前端MVC框架能夠作到和任何服務端語言配合呢?這個解決手段之一我在前文中的第一階段先後端分離方案裏就提到了,那就是解決報文格式的統一和交互接口的統一的技術手段,只有這樣前端MVC和服務端的靈活對接就不會再是問題了。可是僅僅這個手段仍是遠遠不夠的,咱們要達到這個需求還須要解決一個問題,這個問題就是要把服務端MVC霸佔web前端的工做也要搶回來。那如何搶呢?

  上篇文章裏我分析過服務端MVC的視圖層的問題,服務端MVC的視圖層技術例如java裏的jsp技術,這個技術是將html和java代碼整合的技 術,java的web容器把jsp解析完畢後最終生成爲html文件發送給瀏覽器,瀏覽器在解析這個html將最終效果展現給用戶。那麼咱們要搶回服務端 霸佔的web前端的工做咱們就得分析下這些動態頁面技術到底作了哪些事情特別是侵佔web前端的事情。

  這裏首先咱們要談談服務端在動態頁面裏的做用,其實服務端爲動態頁面做用很單一就是提供了網站須要展現的數據而已,服務端是不會創造一個新頁面的。服 務端提供的數據的類型也是很統一,要不就是服務端語言提供的基本數據類型例如:字符、數字、日期等等,要不就是複雜點的數據類型例如數組、列表、鍵值對等 等,不過歸屬服務端的動態頁面還須要服務端語言幫助作一件事情,那就是把服務端提供的數據整合到頁面裏,最終產生一個瀏覽器能夠解析的html網頁,這個 操做無非就是使用服務端語言能夠構造文件的能力構建一個符合要求的html文件而已。不過一個頁面裏須要動態變化的每每只是其中一部分,因此作服務端的動 態頁面開發時候咱們能夠直接寫html代碼,這些html代碼就等於在構造頁面展現的模板而已,而模板的空白處則是使用服務端數據填充,所以在java的 web開發裏視圖層技術延生出了velocity,freemark這樣的技術,咱們將其稱之爲模板語言的由來。

  因而可知,服務端MVC框架裏搶奪的web前端的工做就是搶佔了構建html模板的工做,那麼咱們在設計web前端的MVC框架時候對於和服務端對接 這塊只須要讓服務端保持提供數據的特性便可。從這些論述裏咱們發現了,其實前端MVC框架要解決的核心問題應該有這兩個,它們分別是:

  核心問題一:讓模板技術交由瀏覽器來作,讓服務端只提供單純的數據服務。

  核心問題二:模板技術交由瀏覽器來承擔,那麼頁面的動態性體現也就是根據不一樣的服務端數據進行頁面部分刷新來完成的。

  而這兩個核心問題解決辦法那就是使用ajax技術,ajax技術天生就符合解決這些問題的技術手段了。

  要讓web前端承擔模板技術,就得使用javascript的模板技術,時下javascript的模板技術可謂是百花齊放,百家爭鳴,不少朋友曾爲 這些技術稱奇,其實探求它的本源無非就是用javascript爲基礎實現了個jsp,velocity而已,若是有朋友還沒接觸過javascript 模板技術,能夠在百度裏搜索下【javascript模板引擎】,本文這裏就不展開談論了。

  前端的MVC討論到這裏又出現了一個新的疑問,我上面講到解決前端MVC兩大核心問題的手段是ajax技術,ajax是異步請求,那麼這是否是就是說讓網站所有使用異步請求咱們就能夠實現前端MVC,而且解決網站全部的問題呢?

  這個問題的回答固然是不可能的。一個網站是永遠無法擺脫與異步請求相對的同步請求,就算有個網站把異步作到了極致,可是它也沒法擺脫用戶第一次訪問要 在瀏覽器地址欄填寫網站入口頁面url地址的同步請求問題,網站把異步操做作到極致也無非就是把網站作成了一個純粹的單頁面形式而已。

  純粹單頁面的網站不少人一聽到就以爲好牛逼啊,很前衛,很厲害,對前端有所瞭解的人還會想到單頁面也就意味要運用更多的javascript編程和 DOM編程,前端代碼難度也會大大加強,好的單頁面應用若是這個應用還包含複雜的業務邏輯,那麼單頁面前端開發裏極可能還會使用到現在很火爆的 javascript模塊技術例如requirejs或者seajs技術,單頁面聽起來實在太完美了,可是咱們冷靜下來思考下,單頁面真的完美嗎?下面我 要爲單頁面潑潑涼水了,具體以下:

  潑涼水一:單頁面其實指的是網站只有一個入口,可是並不表明用戶看到的網頁就是一個樣子的,單頁面裏也會有不少 頁面切換,可是無論頁面裏的模樣如何變化,瀏覽器地址欄的地址都不會變化,能作到這點就得歸功ajax的超強能力了,單頁面不一樣模樣的展現都是在 javascript代碼裏實現的,那麼問題來了,單頁面對於搜索引擎的網絡爬蟲就很是不友好了,由於網絡爬蟲是根據url抓取頁面,抓取完畢後會忽略 javascript代碼,那麼單頁面的設計方案就會致使SEO優化只能做用於首頁,而網站其餘頁面將無非有效的被SEO技術進行優化。

  潑涼水二:一個網站作成單頁面之後那麼網站不一樣的展現都在一個url下面,可是若是有些用戶只是對網站的某一部 分功能很感興趣,而這部分功能又不是被單頁面的惟一同步請求所展現的首頁裏的內容,那麼結果就是這些用戶每次登錄網站時候都要手動操做一下才能進入本身想 要的功能頁面裏,假如首頁進入功能頁面的操做步驟比較繁瑣,那麼這個必然會致使網站用戶體驗的降低。

  那麼上面的問題該如何來破呢?

  這裏我首先來說講第二個問題的解決方案,第二個潑涼水的問題的核心就是要記錄單頁面的狀態問題,這個狀態能夠幫助首頁能快速切換到具體的功能頁面,要 讓客戶端網頁有狀態最經常使用的手段就是cookie了,若是瀏覽器支持html5,那麼保存狀態的手段就更多,能力也更強了。可是這種手段是和客戶端緊耦合 的,那麼若是碰到這種狀況,該手段就會出現問題了,例如若是有我的發現單頁面網站裏一個頗有趣的功能,這時候他正好和朋友QQ聊天,他告訴了他的朋友,他 的朋友也該興趣,讓他把連接發過來,那麼這個朋友就不得不在從首頁在重複操做一遍,因而可知,cookie的手段並無全面解決這個問題,那咱們還有其餘 手段嘛?

  答案是還真有,那就是使用html的錨連接,錨連接的形式以下所示:

http://www.baidu.com/#sharpxiajun

  下面是我摘抄下百度百科對錨連接的解釋:

錨連接實際上就是連接文本,又叫錨文本。能夠理解爲:帶有文本的超連接,就叫錨連接。錨文本能夠做爲文本連接所在的頁面的內容的評估。

通常的來說,網站頁面中增長的錨連接都和頁面自己的內容有必定的必然聯繫。網站建設的行業網站上會增長一些同行網站的連接或者一些作網站建設的知名設計網站的連接;

另外一方面,錨文本能做爲對所指向頁面的評估。錨文本能精確的描述所指向頁面的內容,我的網站上增長Google的連接,錨文本爲 「搜索引擎」。這樣經過錨文本自己就能知道,Google是搜索引擎。

  那麼在單頁面裏的功能切換時候咱們改變一下url上的錨文字,反過來講使用錨文字作路由器,讓其能夠路由到對應的功能頁面那麼上面的問題不就能夠解決 了。關於錨連接我這裏要補充一些知識,首先錨連接的形式是url#文字,錨的起始標記是#號,這個#號的內容實際上是屬於瀏覽器端的,也就是說#包括#號後 面的內容是不會被髮送到服務端的,那麼咱們想改變錨連接只能在客戶端進行,可是傳統的錨連接的變化是很難被javascript語言監控到的,直到 html5的出現才從根本上解決了這個問題,html5提供了hashchange事件,該事件能夠監控錨連接的變化,由於javascript語言能夠 監控錨連接的變化,那麼使用錨連接路由功能頁面就成爲了可能,那麼低版本的瀏覽器該怎麼辦了?這個主要是ie的問題了,其實ie8包括ie8都支持 hashchange事件,再低就不行了,不過jQuery有個插件可讓低版本的ie支持hashchange事件,有興趣的童鞋能夠百度下啊。

  看來潑涼水二問題是有解的,那麼潑涼水一怎麼解決了?個人回答是基本無解,這個問題的關鍵在網絡爬蟲這邊,若是咱們被動解決這個問題,那隻能是拋棄 javascript了,這個玩笑就開大了,因此咱們只好祈求各大搜索引擎能不能智能化再厲害點了。這裏加個題外話,我最近幾天忽然意識到一個問題,那就 是講到web前端技術我必定要增強對SEO的思考,由於絕大多數網站都會把搜素引擎當作入口的生命線,這是一個很難迴避的問題,無論咱們網站作的如何優 秀,假如用戶很難找到它,那一切都將會是百搭,而在前端設計裏要加入SEO的思考,這必然會致使整個架構的重大變化。這個問題我會在以網站靜態化角度審視 先後端分離方案時候重點講下。

  前端MVC討論到這裏咱們會發現咱們的談論裏缺了一環那就是MVC的M層模型層,web前端要侵入到模型層了,這不就等於web前端要造反了,它不只 僅想改變從屬服務端的悲慘命運,還要搶奪服務端的部分功能,讓服務端成爲瀏覽器對應的存儲系統,這不是無異於虎口奪食,在時下服務端如此強勢的大環境下, 這種想法簡直就是活得不耐煩了,哈哈,固然這是戲言了,作技術作工程仍是要講求個合理性和邏輯性的,技術和工程都是實在的東西很講道理的,只要道理站得住 腳怎麼個作法都是其次,回到問題自己,我我的以爲在PC端討論web前端作模型層其實每每利大於弊,就安全而言,模型層意味有大量業務邏輯推移到web前 端,那麼安全的保障難度會加大,就技術難度而言,web前端作模型層會讓javascript編程巨複雜,因此要作這個抉擇時候必定要結合業務作仔細的權 衡,其實我如今接觸的一些說包含模型層能力的前端框架在實際運用裏模型層的功能仍是使用太少,不過這個問題若是放到移動端,或者是PC和移動端融合可能就 會有些不一樣,這個問題我將在本系列的終篇裏再談談,這裏也不累述了。

  說到這裏須要總結下了,前端的MVC不該該等於單頁面開發,前端MVC也不是把ajajx用到極致,根據實際業務場景,咱們須要適當的把同步請求和異 步請求結合起來。若是前端MVC裏包含了更多同步請求,那麼對於MVC裏的C層即控制層就會有更高的要求。先後端分離主題還有個下篇,下篇裏我還會提到一 種先後端分離方案那就是nodejs的運用,而nodejs的運用就是和控制層有密切的關係,上篇裏我提到nodejs是先後端分離方案的催化劑,其實我 我的認爲nodejs參入的先後端分離方案纔是更加完美些的先後端分離方案,這個完美的評價緣由之一就是從前端承擔控制層做用角度思考的,因此前端控制層 這個內容我將放在下篇討論。

  好了,本篇寫完了,從本篇咱們能夠看到前端MVC的歷史很早,它的出現早於nodejs,這就說明前端MVC其實並非什麼新技術,只不過是如今才被 你們重視起來,完善它的人也愈來愈多。從本篇咱們還發現前端MVC其實並不完美,問題不少,最致命的就是對網絡爬蟲的不友好,因此咱們須要考慮到SEO技 術參入其中的先後端分離方案。

 

網站靜態化處理—先後端分離—下(8)

我第一次據說nodejs技術大概是在2009年年底,不過我真正認真在網絡上進一步瞭解nodejs仍是在2010年年中,當時對nodejs的 認識和我如今對nodejs的認識有着天壤的區別,開始想了解nodejs我只是爲了感慨谷歌公司開發的V8引擎竟然如此強大,它不只僅能夠做爲 chrome瀏覽器的javascript內核運行平臺,竟然還能爲服務端使用javascript語言做爲平臺,經過對nodejs的瞭解讓我認識到 chrome瀏覽器是如此的優秀,可是如此相對的是我並不認爲javascript做爲服務端語言真的會有市場。

  爲何我當時會認爲javascript做爲服務端語言的前景堪憂呢?我當時有以下的思考,這些思考放到時下nodejs已經很是火爆的背景下,我相信對不少朋友任然有參考意義,下面是我當時的思考,具體以下:

  質疑nodejs思考一:2010年以前我還不是敢自稱本身是一名專業web前端的工程師,所以對於javascript的 認識和掌握程度也不能和如今相比,可是對於javascript的難學,難深刻倒是有着切膚之痛,所以我想javascript做爲服務端語言就是讓會其 他服務端語言的工程師更加深刻的學習常被服務端工程師詬病的javascript,這麼作的結果無異於逼迫服務端工程師轉向成web前端工程師嘛,這個想 想就讓人以爲不現實。

  質疑nodejs思考二:我對web應用開發的技術選型認識比較膚淺。技術的選型是個很寬泛的問題,回到我對nodejs的質疑思考主要是體如今web應用服務端語言選擇上,在中國用做web服務端開發的語言很是多,可是主流的無非就是java、php、C#以及C語言系列,固然web服務端技術發展到如今Python、ruby也是有必定市場,做爲一名具體幹活的軟件工程師對 於項目選擇何種技術是沒啥發言權的,所以我經常以爲技術選型就是項目經理或者是技術經理以及架構師的問題,而大多時候咱們去詢問爲何用這個服務端語言得 到的答案都是非技術性的回答,例如:公司主要是使用php啊,java比較流行人好找啊,C#開發快啊能很快的完成工做,不多有人會這麼告訴你咱們的項目 是個什麼樣的項目,這個項目使用A語言比使用其餘的B語言、X語言有何種好處和優點,其實中國不少軟件企業作項目在技術選型這塊都很粗,說的難聽點其實就 是不少能控制項目的人技術水平很難被恭維,固然大部分項目其實使用什麼技術實現並非過重要的問題,可是這個到了技術架構異常複雜的大型網站技術選型問題 就顯得尤其重要,這個認識主要是來自於我閱讀《淘寶技術這十年》所感覺到的,淘寶網站的技術選型隨着業務的發展變化的如此之大,顛覆性如此之高,這個在我 待過的不少項目組都是難以使人想象的。

  Web應用發展這麼多年,那些佔據了天時、地利和人和的現有技術基本都是處於一個壟斷的地位,新的同類型語言想突破重圍必然有着本身獨有的技術優點, 這就比如在中國作互聯網若是有家新型互聯網公司能夠突破BAT的圍追堵截,那麼這家公司必定是有着本身得天獨厚的優點,因此nodejs必定是得到一種得 天獨厚的優點,那麼nodejs優點在哪裏了?不過在講述nodejs的優點以前咱們先來說講上篇文章裏遺留下來的問題。

  其實上篇裏我講到前端MVC,文章裏只是着重講到了V層即視圖層和M層即模型層的問題,而惟獨沒有專門講解C層即控制層的問題。在先後端分離文章第一 篇裏,我談到若是把MVC框架裏的C層以做爲鏈接web前端和web服務端的角度來理解,C層主要承擔了三個方面的工做,它們分別是:路由、報文格式轉化和頁面渲染的工做。 前端MVC在處理報文格式轉化和頁面渲染這兩個方面仍是比較容易作到,可是在作路由這塊存在必定問題,前端MVC框架對於獲取服務端數據這塊以及異步請求 處理方面其實和傳統MVC框架的處理的手段本質上是相似,只是實現載體有所不一樣而已,可是控制層還有一個路由功能,其中用於頁面切換的路由存在必定的問 題,不過這個切換也要限定一下範圍,頁面經過ajax技術讓頁面部分刷新,假如這種部分刷新讓頁面展現效果發生很大變化,對於用戶而言也是頁面發生了切 換,可是這種切換是不會讓地址欄的url產生任何改變,這就是問題的根本所在了,我在上篇裏已經討論過這些問題,經過這些問題咱們發現若是頁面轉化時候地 址欄的地址隨之也發生改變是會給用戶體驗、網站的友好度以及SEO優化帶來好處的,如是乎我提供了一種手段,那就是使用錨連接來幫助咱們實現url的變化,由於錨連接只是做用於瀏覽器,所以這種手段是對前端MVC的C層實現頁面路由功能的一種很好的支持,可是由於這種方式須要在javascript裏完成,那麼對於SEO優化就產生了問題,最後我提出了頁面切換咱們最好使用同步請求的方式。

  這個時候問題來了,若是要使用同步請求,那麼這個同步操做天然是要讓服務端來控制,這麼作的結果就是讓服務端再去回收部分控制層的功能,這樣下來一個 使用前端MVC架構的網站就有點不太純粹了,具體點就不是一個單頁面的網站,這裏咱們的討論又迴歸到了單頁面的問題了,前文講前端MVC框架不少熱心網友 對個人論述發表了有價值的評論,可是我發現個人想法有些朋友可能沒有真正理解(這也許是個人表述的問題吧),我前端MVC講述的一個思路是以批判前端 MVC的角度進行的,我早些時候和一些網友探討過前端MVC的設計問題,有些朋友在沒有作具體web前端MVC架構前老是想實現純粹的前端MVC框架,延 着這些朋友的思路咱們就會把全部的C層和M層的東西都移到web前端,我常想若是真的這麼實現了,結果天然就是單頁面網站了,或者就是在前端引入了複雜的 模型層設計,不過探討畢竟是探討真的實現時候不少朋友就會知道了難度所在了,因此說理想和現實是有差距的,這話又一次靈驗了。

  這種理想和現實的差距,其實就告訴咱們必定有個地方出問題了,那麼問題在哪裏了?下面我將我對這個問題的思考,總結以下:

  問題思考一:讓前端承擔大部分MVC的工做,那麼前端自己的技術能力是否能達到全部的要求嗎?這個回答彷佛是確定的,例如單頁面的出現就表明了這種可能性,javascript也是擁有強大的面向對象的編程能力,所以再寫複雜的業務模型層也是沒問題,可是前端這麼作了之後其實並不能知足全部人的需求,例如:SEO的要求,SEO不少技術都是以同步網站請求技術爲根基,這個和前端MVC框架以ajax技術爲根基產生了衝突,這就讓前端技術產生了侷限性。使用javascript面向對象技術來實現業務模型,這個也是有問題,javascript的面向對象的學習成本和精通難度超出了傳統的面向對象的語言例如像java這樣的語言,並且javascript要設計和寫出更加容易維護的代碼是很是不容易的,這麼作不符合我在存儲系列裏講到的要用最簡單的方式實現的原則,這其實也是說明前端技術能力不足的問題。

  問題思考二:其實無論什麼形式的先後端分離方案它最根本的思想就是讓先後端進行解耦,讓不一樣技術語言體系下的人 能作到工做的隔離,最後協同起來各自發揮出本身的最大價值,可是若是咱們只是按前端,後端的角度來作分離,是否是有點粒度過粗,考慮是否是過於片面了?特 別是這個片面的問題,web應用的問題並非一個純技術問題,而是一個技術和業務結合的問題,所以任何應用於生產的技術方案都會受到業務的影響,例如上面 當咱們要考慮SEO的問題,考慮開發難度的問題,那麼純粹的前端MVC的框架就會顯示出本身的侷限性。前端技術沒法改變瀏覽器地址欄的url,這個從不少 角度思考是個合理的設計,可是到了前端MVC裏對C層的設計而言則變成了一種技術手段的侷限性了。由於這種侷限性就讓咱們不得不回到問題的原點狀態,例如 頁面的同步請求,而同步請求最合理的控制地點就是服務端了。

  問題思考三:本思考是一個延生性的思考,我從事這麼多年的web開發,我其實一直困惑於web應用開發和MVC 的關係,爲何咱們作web應用開發時候都要那麼強調和重視MVC設計思想呢?難道web應用開發的世界沒有MVC就不能活了嗎?回首下web應用發展的 歷史,在web應用開發的忙慌年代,的確是看不到MVC的影子,那個時代的確很自由,自由到許多web應用混亂不堪,質量和健壯性差的不能再差了,這個時 候一個英雄出現了那就是MVC,MVC表明了一種次序,一種基本的法則,這就比如人類社會創建的根本原則同樣,這些原則讓人類和野獸有了區別,人類也由於 這些原則而成爲萬物之靈長,相比之下MVC就是web開發世界裏的遊戲規則和行爲準則,所以只有當咱們從MVC角度思考web應用的建設,纔會讓web應 用更加的優秀,這也就是在講述先後端分離技術時候我都是以MVC思想做爲準則進行思考的。思考回到具體的場景,MVC思想的運用就是讓咱們把web應用開 發裏能夠歸爲一類的場景彙集在一個範疇之下,不一樣範疇使用一種雙發均可以接受的統一準則進行溝通,這麼一來咱們就把須要解決的問題簡單化了,各個獨立的範 疇由於減小許多沒必要要的干擾,所以能讓它們發揮出更大的潛力,更重要的MVC還讓web應用伸縮性,健壯性,可維護性大大加強。例如在不少傳統web應用 開發裏在控制層這塊先後端的矛盾就是屬於MVC規則使用不完善所致。

  單頁面的應用存在不少問題,所以須要同步請求的介入,這就致使了服務端再度回收了失去的控制層的功能,這麼作也無可厚非,可是我很擔憂這個改進的引入 會不會致使傳統MVC框架裏控制層的混亂問題,根據個人經驗,這種混亂的程度已經下降了不少不少,基本咱們能夠忽視原來C層的問題了。

  不過不少有追求的web前端工程師對於這種不純粹的前端MVC的異議仍是不少的,大部分異議仍是源自瀏覽器能力的侷限性,當服務端不少方面被弱化後,也許能夠解決咱們之前在前端被服務端束縛的不少問題,可是同時又產生了新的問題,這些新問題我總結以下:

  新問題一:在傳統的網站動靜劃分裏,咱們常把瀏覽器端的技術html、css和javascript歸結於靜態技術的範疇,若是網站使用Web前端MVC那麼前端就會接過不少動態網站的功能,這個時候傳統的靜態技術就被人爲的演變爲動態技術。回顧網站的發展歷程,基本是從靜態到動態的轉變,這個結論用在時下其實已經有點不太對了,隨着網站愈來愈龐大愈來愈複雜,網站技術發展逐漸開始逆向進行了,網站從動態化向靜態化轉變的需求變得愈來愈強烈了,這也是時下前沿的前端技術正在解決的問題,例如本系列的主題網站靜態化技術就是順應這個發展趨勢而來的,因此前端MVC框架在這點上有點逆歷史潮流的問題了。

  新問題二:前端MVC讓web前端的技術難度和架構難度成指數級上升,而javascript語 言天生有着本身設計的缺陷,這個缺陷在寫大規模複雜應用時候就顯得尤其突出,例如:javascript沒有模塊化管理,javascript面向對象的 實現難度,因此前端MVC的應用可能會變相的提高企業的技術成本和開發成本,固然不少新的技術手段能解決javascript固有的缺陷,對這些新技術有 個更大的問題就是「你會嗎「,不會的話首先要解決會的問題,這也是個成本問題。

  新問題三:當前端真的愈來愈獨立於服務端後,這會致使服務端一些能夠優化web前端的重要技術就很難實現了,例如網站靜態化系列裏講到了緩存運用,CDN的運用就很難達到預期效果,或者根本無法使用,由於這些技術的根基都是認爲網站動態性是由服務端發生的,而客戶端霸佔了動態性,那麼這些技術的做用就被限制住了。

  由此我能夠下個結論:若是先後端分離方案是以瀏覽器和服務器角度來劃分並非最好的前端分離方案,那麼先後端分離方案還有沒有新的解決思路了?這個真的有,那就是nodejs參入的先後端分離方案。

  其實先後端分離的驅動永遠都是前端強於服務端,而先後端分離的重要目的也是要給web前端創造一個更加乾淨的開發環境,那麼寫的代碼是不是在瀏覽器上跑仍是在服務端上跑這個並非過重要,因此引入nodejs,就是讓服務端也能跑javascript代 碼並不會是讓人沒法接受的事情,回到先後端分離方案裏以服務端驅動的先後端分離方案,我曾說過這個方案能得到服務端開發人員更多的掌聲,我相信這個掌聲不 會是服務端爲前端的喝彩,而是服務端終於從web前端解脫出來了,這樣服務端運用更加高級的SOA技術就成爲了可能,那麼咱們把web前端的控制層使用 nodejs替代,這麼一來咱們既能夠繼承全部傳統MVC框架的優勢,同時也達到之前後端分離的根本的問題就是爲web前端創造一個很乾淨的開發環境問 題,那麼咱們在前端MVC框架使用時候遇到的問題都會很好的被解決。

  Nodejs的運用讓動態網站的動態性再度停留在了服務端,那麼我前面講的那麼多網站靜態化技術就能夠和先後端分離方案很好的融合了,所以本篇先不具體討論nodejs作先後端分離的實現手段了,在下篇講從網站靜態化角度從新審視先後端分離方案時候一塊兒講解,這麼作會更加符合本系列的主題。

  如今咱們能夠解答爲何nodejs技術能夠突破傳統服務端技術的包圍,由於nodejs可讓先後端達到更高程度的分離,從而讓先後端各自發揮本身的優點,頗有意思的是,雖然nodejs技術屬於服務端範疇,可是它倒是前端工程師驅動來普及的,這絕對是web前端逆襲啊。

 

 

網站靜態化處理—知足靜態化的先後端分離(9)

先後端分離的主題雖然講完了,可是先後端分離的內容並無結束,本篇將繼續先後端分離的問題,只不過此次先後端分離的講述將會圍繞着本系列的主題網 站靜態化進行。在講本篇主題以前,我須要糾正一下先後端分離主題講述中會讓朋友們產生誤導的地方,這種誤導就是對時下流行的一些先後端分離方案(沒有使用 nodejs的先後端分離方案)的評價問題,其實本人任然以爲無論什麼樣的先後端分離方案只要成功被實施,而且產生了良好的效果,那麼它就是一個成功的前 後端分離方案,前面我以一種批判的角度講述這些先後端分離方案,並非想在否認它們,而是出於一種雞蛋裏挑骨頭的較真態度想從新審視這些方案,但願這種審 視能讓咱們的設計方案變得更加優秀,同時本身也在這個較勁的過程裏獲得自身技術能力的提高。其實那些被我批判的技術方案也許在某些特定場景下它就會變的更 加優秀,我推崇的技術方案在某些場景下可能就變的蒼白而無力,這種狀況頗有可能發生,不說別的,我之因此批評前端MVC,其私心就是由於它不符合網站靜態 化的處理,若是把前端MVC內容放置在網站靜態化的主題下談論,被批的命運那是必然的。

  網站靜態化技術相對於先後端分離技術的關注度要低的多,若是業界的一些公司由於看了本人的文章能對網站靜態化技術有一種新的認識,從而考慮在本身網站 上使用網站靜態化技術,同時也想實現先後端分離技術,那麼新的問題出現了,這兩種技術同時使用會發生矛盾嗎?若是有矛盾,咱們到底將如何解決這些矛盾?解 決這些矛盾的時候咱們是否是能夠作好二者的兼顧,而不會發生其中一方妥協於另外一方,最終致使其中一方沒有充分的發揮本身的能力。要解答上面的一系列問題, 我首先要探求的就是網站靜態化技術和先後端分離方案裏那些方面會產生矛盾。

  從我前面對網站靜態化技術的闡述,咱們知道網站靜態化的技術最佳做用位置應該是服務端而非是瀏覽器端,之因此會這樣是由於網站靜態化技術的技術基礎是 動靜分離和緩存,這兩個方面若是落到瀏覽器端會碰到不少難以解決的問題,那麼咱們要分析下這些難以解決的問題,具體以下:

  瀏覽器之緩存問題:瀏覽器也有緩存,不過瀏覽器端的緩存那就不是指內存裏的緩存,而是持久化的緩存,實際上瀏覽 器端的緩存很是不可靠,會被不少非技術的因素所限制,例如咱們手動刪除緩存或者使用無痕模式上網,那麼這些持久化的緩存就會失效,用戶再度訪問網站時候都 將是第一次訪問這個網站,這就使得不少優秀的緩存策略方案在瀏覽器端實施效果大打折扣。

  瀏覽器之動靜分離問題:網站靜態化技術裏一個重要的手段就是如何設計動靜分離策略,純粹的靜態內容這個沒啥好說 的,可是動態的內容在必定的條件(例如:時間,一些業務屬性例如商戶屬性)下是能夠轉化爲靜態內容,這些內容若是能被有效緩存,對網站性能提高是不可估量 的,並且這種動靜轉化的策略也能夠減小業務服務器上處理沒必要要的請求,從而減輕業務服務器的壓力,達到提高後臺核心業務服務端的負載壓力。可是若是咱們使 用前端MVC框架,一股腦子把不少服務端功能往前端遷移,那麼這種動靜處理手段就很難作,並且不少場景基本上是沒法應用了。

  所以我認爲先後端分離方案使用nodejs價值更高,由於使用nodejs咱們就能夠根據網站靜態化技術將須要保留在服務端的功能能夠繼續保留在服務 端,這樣就能達到兩者兼顧的目的。可是若是咱們認爲把nodejs引入後,nodejs的目的就是用來作網站總體MVC架構下的C層即控制層,這個思路到 底合理不合理呢?這個問題仍是很值得玩味的,所以咱們須要分析下網站總體MVC架構下的C層即控制層的做用。

  在前面文章裏我曾總結過C層即控制層在MVC框架裏的做用,這個做用分別是:路由、報文格式轉化以及頁面渲染,可是這個做用的總結我是有個前提條件 的,那就是以C層即控制層做爲先後端溝通介質的前提下。若是先後端分離方案引入後把控制層歸爲前端的組成部分,那麼控制層跟前端的結合問題都是人民內部的 矛盾,都是比較好解決,可是控制層就僅僅是用來鏈接先後端一個做用嗎?對於網站架構裏的控制層,有一個不可避免的功能那就是做爲後端服務端的安全入口的做 用,也就是說控制層是作請求安全檢查和安全監控的地方,並且不少安全校驗還會和業務相關,例如檢查報文是否被篡改啊,防釣魚的功能,若是這些功能被前端來 承擔,首先不談前端技術人員會不會作這些,可是至少一點問題是會發生的,前端工程師在關心頁面開始同時還要寫服務端的業務邏輯了,無論怎麼說,這些功能遷 移到前端總不是太合適。當網站演變爲超大型網站後,大型網站每每是不少小中型網站項目的集合體,爲了減小網站總體的異構性,咱們經常把不一樣的模塊網站的入 口整合在一個大型控制層項目下面,這個大型控制層項目通常稱爲網關項目,它的做用和網絡裏的網關很是類似。除此以外,還有些網站的控制層很是特別,例如一 些作第三方支付的網站,那麼這樣網站項目自己就是個大網關,並且這個網關很特別,它後臺的服務就是其餘銀行的系統,它的路由工做就會變得異常複雜,例如: 根據用戶使用銀行的不一樣,控制層要組裝不一樣的報文信息,而這些功能都是屬於控制層,這樣的場景無疑大幅度提高了控制層再和模型層對接的技術難度,而增長的 難度問題又和模型層耦合度很高,因而可知,web應用總體的MVC的控制層比咱們想象中要複雜的多。

  回到用nodejs替代控制層這個主題,咱們來看看實際的場景吧,假如咱們的網站控制層相對比較簡單,好了,這時候咱們跟領導或老闆說「如今很流行前 後端分離,咱們項目也使用下先後端分離技術」,領導或老闆一聽可能會爲之一振,那麼就會問你」那麼該怎麼作了」,你這時對他說「首先把控制層用 nodejs重寫下」,領導或老闆聽到這個回答他會贊成你這麼幹嗎?一個不會給網站增長任何新功能,同時不能很直接有效的提高網站的性能,並且執行它還會 有很大風險的方案,頭兒們會贊成嗎?好了,假如你終於找到合理理由說服頭兒們,那麼若是咱們的網站規模已經很大,控制層已經演變成了網關項目,控制層自己 已經巨複雜了,你敢用nodejs重寫一遍網關項目嗎?因此說吧nodejs直接當作控制層,其實實踐起來困難重重,並且nodejs徹底承擔控制層,它 的性能,它可否很好的運用於集羣開發這都是很難把控的問題。分析到這裏,咱們彷佛又進入了死衚衕了,那如何來破這個局呢?

  上面的問題只是反映出整個網站MVC裏的控制層其實還有部分功能是和服務端的模型層緊耦合的,所以要解決這個問題就是把傳統的控制層再細分一下,屬於 前端的部分劃分給web前端做爲web前端的控制層,屬於服務端的部分任然留給服務端,這麼拆分後,當咱們引入了以nodejs爲基礎的先後端分離方案, 服務端的控制層改造無非就是去掉頁面路由,頁面渲染,再修改下返回數據格式便可,由於不用修改服務端的業務代碼,其代價是很低的,頭兒們也很容易接受這樣 的方案,並支持咱們大膽去嘗試新技術。

  服務端網站靜態化技術SSI和ESI,主要是根據動靜分離策略把網頁不會常常變化的模板進行緩存,而後在靜態資源服務器位置整合動靜資源,若是咱們使 用nodejs只是簡單替換原來的控制層,那麼這些策略其實仍是有問題的,那麼怎樣作可讓nodejs兼容SSI和ESI了?這裏我列舉個實際的案 例,nodejs有一個模板語言叫作jade,nodejs裏還有個技術叫作handlebarsjs,其中handlebarsjs和struts的標 籤相似,它能夠處理一些簡單的業務邏輯,咱們開發時候使用jade編寫頁面的模板,使用handlebarsjs讓動態數據和模板進行整合,項目發佈時 候,使用像grunt這樣的項目管理工具編譯項目,jade文件變成html文件,而handlebarsjs則會轉化爲javascript代碼,這樣 咱們就能夠把生成的html文件在服務端進行有效緩存,而handlebars生成的javascript文件負責整合動靜數據,這樣nodejs就能夠 達到兼容SSI和ESI的做用了。

  不過引入nodejs會讓網站處理請求的過程裏增長一個環節,這樣可能會致使部分性能的損失,可是我上面的實例卻能有另外的方式規避這個問題,由於 nodejs的代碼是用javascript語言編寫的,那麼這個代碼是能夠運行在瀏覽器上的,那麼這就會產生了一個處理手法,那就是咱們在生產部署時候 其實不須要部署nodejs的,咱們把靜態模板就緩存在服務端或者推送到CDN上,而後handlebarsjs生成的js代碼就讓它傳送到瀏覽器端,因 爲這個js代碼生成後基本不會變化,瀏覽器能夠緩存它,固然CDN或靜態資源服務器也能夠緩存它,其實它在瀏覽器運行時候變化無非就是獲取一次服務端數據 而已。這麼一來,生產上的web前端又轉變成了前端MVC的形式,還把動靜整合的事情交由了瀏覽器來完成,這不只是兼顧的網站靜態化要求,還讓動靜整合推 到了更加靠前的瀏覽器端,這不是達到了一個共贏的效果了嘛。

 

網站靜態化處理—反向代理(10)

反向代理也是一種能夠幫助實現網站靜態化的重要技術,今天我就來說講反向代理這個主題。那麼首先咱們要了解下什麼是反向代理。和反向代理相對應的是 正向代理,正向代理也就是咱們常說的代理服務,正向代理是很是常見的,例如在某些公司裏咱們想使用互聯網,那麼咱們就得在瀏覽器裏設置一個代理服務器,通 過代理服務器咱們才能正常使用互聯網,而這個代理服務器就是一個正向代理服務器。正向代理更加讓人熟悉的使用場景估計仍是在FQ技術裏的使用,咱們使用一 個放置在國外的代理服務器來訪問那些在國內沒法正常訪問的網站,這其實也是在使用一個正向代理服務。

  其實無論是正向代理仍是反向代理,這兩個概念的定義都是以瀏覽器側爲基準進行的,正向代理是代理瀏覽器來訪問互聯網,反向代理是指代理再也不是代理瀏覽 器側了,而是反過來代理瀏覽器須要訪問的應用服務器。那爲何咱們要使用正向代理服務器了?答案固然不是爲了FQ了,下面我來列舉些實例來講明這個問題 了。

  例如公司裏使用代理服務器主要是爲了安全的考慮,不少公司內部都有本身的局域網,通常咱們稱之爲內網,內網裏有公司的各類資源,若是公司員工的電腦隨 意鏈接到互聯網,假如碰到那些別有用心的黑客,經過攻擊員工的工做電腦截取了公司重要的文件資料,那樣就會形成公司的重大損失,正向代理除了能防範外部的 黑客攻擊外還能監控和控制公司內部員工將公司重要文件經過互聯網傳遞給不恰當的人,所以公司讓員工使用代理上網基本都是出於安全的角度來考慮的。

  正向代理的合理使用還能幫助一些企業提高本身產品的核心競爭力,例如在移動端有一款很是流行的瀏覽器,它之因此很是受用戶的歡迎,是由於使用該瀏覽器 上網速度比其餘瀏覽器明顯的快多了,那麼這款瀏覽器是如何作到這點的呢?奧祕就是這家公司爲本身的瀏覽器對應創建一個十分強大的代理服務器集羣,用戶使用 該瀏覽器訪問網站時候用戶首先訪問的是該公司的代理服務器,而這些代理服務器使用緩存技術緩存了海量的網站信息,再加上使用一些web加速的技術例如 CDN技術,這就讓該瀏覽器訪問網站的效率明顯快於其餘瀏覽器。

  反向代理和正向代理從技術角度上基本上是一致的,區別主要是代理的內容不同了,反向代理代理的是應用服務器。反向代理技術也基本上是互聯網公司的一 個標配技術,可是反向代理可否正確使用,可否更進一步的發揮它的實用價值,我以爲並非全部公司都能作好的,下面我來總結一下反向代理的使用目的吧,具體 以下:

  使用目的一:反向代理能夠隱藏真實的應用服務器。該目的屬於安全的範疇,反向代理隱藏真實的應用服務器,那麼就可讓別有用心的黑客很難掌握正確的應用服務器,從而增長黑客的攻擊難度。

  使用目的二:反向代理能夠實現負載均衡的功能,例如在java的web開發裏有一種很簡單的實現集羣的手段,這個手段就是使用apache加上tomcat的組合,用戶請求先到達前置的apache服務器,apache再使用負載均衡策略將請求分配給後臺不一樣的tomcat服務器上。

  使用目的三:反向代理能夠起到動態調節應用服務器併發數的目的,通常用做反向代理的服務器都是靜態資源服務器, 這樣的服務器在併發處理能力上要遠強於後臺的web應用服務器,那麼能夠經過控制web應用服務器前置的反向代理服務器,這樣就能夠動態調節後臺服務的負 載的大小,這個作法的好處可能不少朋友都不太瞭解,這裏我列舉個例子,一個網站最須要穩定性的部分是哪一個部分呢?不少朋友會說是數據庫, 的確數據庫是最重要的,由於數據庫作分佈式很難,很容易造成單點故障,要是數據庫掛了基本一切都無法玩了,那麼除了數據庫以外還有別的嗎?固然有,那就是 用於處理業務的應用服務器了,應用服務器若是作了集羣,集羣中其中一臺服務器掛了其影響面會比數據庫掛掉低多了,可是一個網站的作業務處理的應用服務器掛 掉,對公司的損失仍是很大的,而web應用服務器前面的用做反向代理的靜態資源服務器掛掉問題就會小多了,至少不會產生公司業務沒法正常完成的事情了,因 此當網站負載太高,讓過載的請求被反向代理攔截或者阻止,這對應用服務器的穩定性提高有莫大的好處。固然反向代理調節應用服務器的負載水平的用途不只僅這 些,有興趣的朋友能夠在網絡上找找相關的介紹。

  使用目的四:反向代理能夠緩存靜態數據,通常用做反向代理的服務器都是使用像apache或者是ngnix這樣的靜態資源服務器,所以咱們能夠把web應用裏的靜態資源緩存在反向代理服務器上,從而達到提高請求處理的速度問題。反向代理的這個功能就和本系列的主題網站靜態化處理切合了。

  分析完反向代理的使用目的後,咱們如今將反向代理應用到項目裏,這裏應用的一個前置限定就是將反向代理應用到網站靜態化的處理之上,首先是第一個應用方式,以下圖所示:

 

      第一種反向代理應用方式就是讓反向代理和應用服務器一一對應,也就是每臺應用服務的部署服務器上都對應部署一臺反向代理服務器,這麼作有怎樣的好處呢?首 先咱們來說第一個好處,若是咱們將網頁作了動靜分離,那麼反向代理服務器就能夠負責對請求中的靜態資源訪問進行處理,同時反向代理還能夠承擔動靜資源整合 的目的。這裏要特別說明下,前文裏我說道動靜資源會由於咱們使用的動靜策略而發生轉化,那麼有些動態內容在必定條件被轉化爲靜態資源後,咱們能夠將這些作 了轉化的靜態資源在服務器上緩存起來,這個時候上圖展現的架構模型就會發生變化,以下圖所示: 

      咱們看到反向代理服務器應用服務器之 間會造成一個cache層,反向代理訪問cache層的效率會比直接訪問應用服務器要高的多,這等因而給應用服務器作了一個加速操做,同時經過緩存咱們可 以減小應用服務器的運算壓力,從而達到提高應用服務器性能的目的。之前有朋友問我這麼作會不會增長應用服務器的壓力,由於一臺服務器上部署了兩臺能夠處理 web請求的服務器,那麼它們之間必定會有發生衝突的時候,不過我想產生衝突確定是咱們沒有很好的處理兩者關係所致,因此咱們要理清在同一臺服務器上部署 反向代理和應用服務器後,它們之間的關係究竟是怎樣的?

     其實反向代理和應用服務器從物理形態角度上它們是兩個不一樣的東西,可是兩者在邏輯上實際上是一個總體,它們共同完成一個邏輯性的應用服務器的功能,只不過 兩者由於應用場景不一樣而造成了一種分工合做的關係,反向代理服務器主要完成對靜態資源請求的處理,而應用服務器則是負責業務邏輯的處理,它們最終造成一個 強大的協力使得總體的邏輯性應用服務器的性能獲得顯著的提高。

     除此以外,這個反向代理還能夠發揮動態調節應用服務器的併發數的目的,可是上面的技術方案卻沒有發揮反向代理的負載均衡以及安全性這兩個方面的做用。爲了讓反向代理四個使用目的獲得充分的發揮,那麼咱們該如何來作了?

     方法很簡單就是把反向代理的部署地點從應用服務器所在的物理服務器上遷移出來,放到一臺獨立的物理服務器上,可是這個作法會有性能上的損失,同時還會增長整個技術架構的複雜性。爲何性能會損失呢?由於原來的反向代理服務器應用服務器部署在同一個物理服務器上,那麼它們之間的通信都是之內存共享的方式進行的,這樣的通信效率是很是高,如今換成了經過網絡通信進行溝通,而網絡通信是IO設備裏效率最差,可靠性最差的,所以單獨部署反向代理服務器或多或少都會形成必定性能的損失。

    爲何說單獨部署反向代理會增長整個網站技 術架構的複雜性了?咱們把反向代理服務器單獨部署,那麼單獨部署時候咱們還會是使用一一對應的策略嗎?先不談這麼作,從技術和業務角度的好處和壞處,但從 成本這個考慮就是會讓不少公司望而卻步,由於這個作法就會致使用於部署應用服務器的成本翻倍的增長,而增長的服務器用於反向代理,這樣的作法怎麼體會都不 是以爲物有所值,再說用於反向代理的靜態資源服務器自己處理請求的併發能力是普通應用服務器的數百倍,一一對應自己也沒有徹底發揮反向代理服務器的潛力,所以最好的解決方法就是把反向代理服務器作成一個反向代理服務器集羣,作成集羣問題又來,集羣裏每臺反向代理緩存的數據是否是要保持一個同步了?這就比如處理應用服務器的 session同步問題,若是真的這麼作會不會致使反向代理服務器上緩存大量使用率不高的數據從而致使緩存的利用率不好,同時同步操做自己也會影響到反向 代理集羣的性能,因此要設計一個好的反向代理集羣是一件十分複雜的事情,其實合理的反向代理集羣的作法就是在集羣裏在進行分組,每一個分組應該是和後端的 SOA服務相匹配,這個時候反向代理集羣的效率才能獲得最大的發揮,同時資源利用率也會更加的合理。其實使用反向代理集羣方式,也會給生產部署形成麻煩, 若是網站進 行了靜態化處理,那麼反向代理須要承擔對靜態資源的處理操做,這個時候反向代理和對應的應用服務器結合起來才能造成一個完整的應用服務器,可是如今咱們將 一個完整的邏輯應用服務器分開部署了,那麼當咱們發佈新應用的時候就得面臨更加複雜的狀況,這就增長了部署和運維的風險和難度。

     我如此批評單獨部署反向代理的問題,可是我並非說這種作法徹底不可取,而是想告訴你們這種作法實際上是一種高級的作法,可是也是一個複雜的作法,要作好這個集羣是很麻煩的一件事情的,我以爲只有當咱們的網站業務量和請求量很大的時候,同時原有方案出現了瓶頸時候能夠認真考慮反向代理集羣方案的實現,不過將反向代理造成集羣會給網站的安全性帶來莫大的好處,反向代理能夠隱藏後臺的應用服務器,這種隱藏就是客戶端只須要訪問代理服務器即 可,應用服務器對外都是以反向代理來展現的,可是若是反向代理和應用服務器一一對應,那麼惡意黑客找準了某臺反向代理服務器後,對這個反向代理服務器進行 反覆的攻擊,那麼這個攻擊也就等於攻擊與之對應的應用服務器,這就致使反向代理隱藏真實應用服務器的做用就沒有獲得有效的發揮,而集羣這塊就能夠很好的處 理這個問題,不過咱們若是以爲使用集羣代價過高,咱們也有變通的方法,那就是在全部邏輯應用服務器前面再放置一個反向代理服務器,這個反向代理服務器再也不承擔緩存的功能,而只是用來作負載均衡和安全處理,這樣一一對應的策略安全性也能夠獲得保證,不過若是公司技術能力好能夠考慮使用LVS這種軟件化的負載均衡技術方案,假如公司還頗有錢還能夠考慮使用更加高級的硬件負載均衡設備例如F5設備。

     若是咱們網站除了使用網站靜態化技術還使用了先後端分離技術,固然這個先後端分離技術應該是使用nodejs的先後端分離技術,那麼nodejs應該放 置在生產部署的什麼位置上了?上篇文章裏我曾列舉了一個nodejs的應用實踐場景,在這個實踐場景裏我曾經提到若是在原有的網站生產架構下引入 nodejs會增長一個請求處理環節,而nodejs使用主要是爲了知足先後端分離而非增長網站性能,所以增長的環節可能會讓請求處理的性能降低,所以我 最後提出一種變通手法,就是nodejs項目發佈時候編譯源代碼,而後將編譯出的javascript 和html文件乾脆推移到瀏覽器端處理,這樣就變相造成了前端MVC框架,這個作法老是有點不三不四的意味,假如咱們真的想把nodejs引入到應用生產 的網絡架構裏,咱們不但願無故的增長請求處理環節,那麼最好是讓nodejs服務器替換某個部分。按照這個思路思考,那麼我以爲nodejs在生產的引入 最好是和反向代理相關,最簡單的方式就是讓nodejs和反向代理一一對應,這樣就能夠很好的下降引入nodejs帶來的問題,固然複雜點的就是反向代理 集羣對應的應用服務器應該是nodejs的應用服務器,而不是用來作業務處理的業務級別的應用服務器。

    無論怎麼說,我認爲在網站靜態化方案裏咱們必定要考慮反向代理的運用,若是靜態化技術方案裏沒有反向代理的身影,那麼這個網站靜態化處理可能很難達到咱們預期的效果。

 

網站靜態化處理—web前端優化—上(11)

網站靜態化處理這個系列立刻就要結束了,今天我要講講本系列最後一個重要的主題web前端優化。在開始談論本主題以前,我想問你們一個問題,網站靜態化處理技術究竟是應該歸屬於web服務端的技術範疇仍是應該歸屬於web前端的技術範疇,要回答清楚這個問題咱們要明確下網站應用的本質究竟是什麼?網 站的本質其實就是BS,這裏的BS我沒有帶上架構二字,而就是指Browser和Server即瀏覽器和服務器,而網站靜態化技術的做用目標就是讓客戶端 即瀏覽器的用戶體驗更好,可是若是咱們想讓網站在瀏覽器上運行的更快,在更快的基礎上能設計更多更好的用戶體驗功能,那麼咱們須要作的工做其實就不只僅是 着眼於瀏覽器自己,而是要把和瀏覽器相關的一切做用因子結合在一塊兒考慮,這就是網站靜態化技術的本源所在,因此有些朋友認爲網站靜態化 技術實際上是一個服務端技術多於web前端的技術,所以認爲網站靜態化技術是不屬於web前端的範疇,我認爲這種理解是不正確的,我想產生這種誤導的緣由是 不少人都是狹義的理解web前端技術,認爲web前端就是以javascript、css以及html所表明的技術,超出這個範疇的技術就不該該屬於 web前端範疇,我我的以爲這種理解也無可厚非,可是這種理解可能會讓那些有追求的前端工程師產生一個很差的後果,這個後果就是不靈活的劃分本身須要掌握 的技術範疇,最終影響自身技術能力的突破,無論是web前端仍是web服務端都應該把作好優秀的網站爲己任。BS自己就是一個總體,只有兩者結合起來才能 產生網站,缺乏其中任何一方,那又何來的網站呢?BS中的S就猶如蝴蝶效益裏蝴蝶的翅膀,雖然蝴蝶看起來只是在亞馬遜雨林輕輕的揮動了一下,但是這個揮動 卻能讓相距千萬裏的太平洋上颳起可怕的颶風,所以本人對web前端有個新的認識,咱們不該該把前端只是侷限於javascript、css和html這些 技術之上,而是應該把本身當作瀏覽器應用開發專家,一切用做於瀏覽器的技術和手段都是web前端工程師須要掌握的知識,就像時下的nodejs出現,逼得 前端工程師不得不去作服務端開發,不要以爲這是被迫的,而要把它當作web前端的逆襲,認爲這是理所固然的事情。

  好了,咱們如今回到web前端優化這個主題吧。Web前端優化技術的普及仍是要歸功於互聯網兩大巨頭雅虎和谷歌的貢獻,他們經過多年的積累和總結,將 這些web前端優化的經驗無償的公佈給全世界,從而推進了web前端的發展,這些技術都不是什麼祕密,我在網上找到一篇講解這些技巧的文章,文章就是《Web前端優化最佳實踐及工具集錦》。

  web前端優化技術和網站靜態化技術使用目的是一致的,就是讓網站變得更快,用戶體驗更好,我我的認爲網站靜態化技術其實就是web前端優化的一部 分,只不過網站靜態化技術是經過服務端的大規模技術改造來實現web前端技術優化,而服務端的這種改造的目的就是讓整個網站的後臺技術架構更加切合web 前端的要求,從而能更好的實現web前端優化。我這裏之因此能如此評價網站靜態化技術,其實說明網站靜態化技術和web前端優化技術必定存在某種強烈的切 合點,我我的認爲這個切合點就是它們背後使用的理論基點是一致的。那麼它們之間這個切合的理論基點究竟是什麼呢?

  優秀的網站應該是用戶體驗好的網站,當人們使用這個網站感受爽,好評不斷,那麼這個網站就是一個用戶體驗優秀的網站,可是用戶體驗好的網站就是網站佈局精美,圖片很炫,人性化設計到位這麼簡單嗎?這些要素都是網站使用者的感覺,可是對於網站設計和開發人員而言,再好的網站必定要解決一個根本問題,那就是網站加載的速度要快,若是網站加載速度不快,你就算把網站設計的再漂亮,估計也會搞的無人問津,說到這裏,是否是有較真的朋友不信個人結論呢?我把前面引用文章裏的一張圖再給你們瞧瞧,以下圖所示:

  其實當咱們開發網站若是隻考慮如何把網站作的漂亮而忽視網站的性能,咱們就會發現漂亮的網站和網站的性能實際上是矛與盾的關係,例如精美的圖片每每須要 高質量的圖片格式,而高質量的圖片格式就意味圖片會變得很大,那麼在圖片經過網絡加載時候就須要花費更多的時間,因此咱們在設計和開發優秀網站時候,漂亮和效率是須要咱們認真權衡的,認真思考的,最終要找到一個最好的方式實現兩者的平衡,同時更加充分的發揮雙方的潛在價值。而直觀的用戶體驗好這其實更多的是一個設計問題,而解決用戶體驗好的根基:速度問題,這就是一個技術問題了。

  要解決網站的速度和效率問題,那麼咱們就得思考網站的載體計算機到底哪些因素會影響網站的速度和效率。其實計算機的本質很簡單,那就是計算和存儲,計算主要是CPU來完成,而計算機用於存儲的介質就多了,它們主要是內存、硬盤,若是是網站應用還有個很關鍵的存儲介質須要考慮那就是網絡了。那麼計算機用於計算和存儲的這些介質的效率是怎樣的一個狀況呢?這個問題我在之前一篇文章裏有過闡述,這篇文章就是《關於如何提升Web服務端併發效率的異步編程技術

  這篇文章的其餘內容太多了,我把關鍵部分在本文摘抄一遍,內容以下:

對於一個網絡請求的處理,是由兩個不一樣類型的操做共同完成,這兩個操做是CPU的計算操做和IO操做,若是咱們以處理效率角度來評判這兩個操做,CPU操做效率是光速的,而IO操做就不盡然了,計算機裏的IO操做就是對存儲數據介質的操做,計算機裏有以下幾個介質能夠存儲數據,它們分別是:CPU的一級緩存、二級緩存、內存、硬盤和網絡,一級緩存存儲和讀取數據的能力接近光速,它比二級緩存快個5倍到6倍,可是無論是一級緩存仍是二級緩存,它們存儲數據量太少了,作不了什麼大事情,下面就是內存了,以一級緩存的效率作參照,一級緩存比內存速度快100多倍,到了硬盤存儲和讀取數據效率就更慢了,一級緩存比硬盤要快1000多萬倍,到了網絡就慢的更不像話了,一級緩存比網絡要快一億多倍,可見一個請求處理的效率瓶頸都是由IO引發的。

  因而可知網站的速度和效率問題彷佛都是由存儲即IO形成的。不過咱們不能由於感受發現問題根源在於存儲,而就忽視對CPU的思考,因此我先講講CPU 和網站性能的關係吧。CPU是計算機用於作計算的設備,如今的電腦能看電影,能聽歌,能夠和朋友聊天,還能用於工做,這些使人稱奇的功能其實到了CPU這 裏也就是經過加減乘除這類基本的數學運算完成的,說到這個真是難以讓人想象,讀書時候學數學老是以爲那麼枯燥乏味,沒想到如此強大的人類神器竟然就是經過 數學運算得來的,難怪有國外科學家說宇宙都是經過數學運算得來的,這仍是有道理的。不過網站背後的數學運算卻有着本身的特色,雖然CPU計算能力很強,但 是在實際場景下不少業務的計算其實很消耗時間的,若是網站某些請求響應背後的運算是須要消耗太多的時間,那麼這個時候CPU也就會成爲網站性能的瓶頸所 在,網站應用有個重要的特色,這個特色有個專有名詞描述那就是網站的實時性,根據網站實時性的特色,那麼就要求咱們網站每一個請求所包含的計算都要簡單和快 捷,簡單快捷的計算也就讓每一個請求背後所包含的業務性運算要更加簡單,這也就是爲何不少人會說互聯網的網站和企業的web應用相比,互聯網的業務邏輯比 較簡單的道理,可是隨着網站的規模擴大,業務模式愈來愈豐富,這個時候網站在某些業務環節不可避免的變得複雜,假如這些複雜的業務又須要實時的反應給用 戶,那麼CPU不能快速完成業務計算就是網站的效率問題的根源了,例如我在存儲系列裏說到的海量數據的計算操做,就是這樣的場景之一,那麼這個時候咱們該 如何來作了?

  碰到這個問題,咱們首先要明確一個問題,計算出現了瓶頸,那麼最直接的手段就是增長計算機的計算能力,好比使用運算更快的CPU,可是更快的CPU面 對快速增加的業務而言,增長的效率是很是有限的,因此在CPU這塊出現了多核技術,咱們能夠把一個計算任務拆分紅諾幹個子運算,這些子運算在不一樣CPU上 計算,最終把結果彙總起來,可是這個手段和用更快的CPU手段同樣,面對快速增加的業務很快就會達到性能瓶頸,最終咱們發現咱們的業務計算任務其實已經超 出了單機計算機的能力,如是乎分佈式技 術出現了,咱們這回再也不是在CPU上作文章了,而是使用多臺計算機聯合計算,可是分佈式計算系統是須要網絡進行互聯的,而網絡是計算和存儲裏最大的短板, 再加以如今互聯網的所使用的計算資源規模達到了超乎想象的程度,咱們發現想經過擴展計算機的計算能力來解決網站快速響應的問題基本是一件沒法完成的任務, 那麼這個時候咱們又該怎麼辦呢?

  這個時候咱們就要轉化思路了,由於當網站的計算瓶頸問題已經到了這個地步了,咱們再去更加深刻挖掘計算機的計算能力這對最終的結果影響已經意義不大 了,所以咱們只能從計算的相關方哪裏尋找問題的解決方案。那什麼是相關方呢?仔細分析計算相關方的確太多了,可是有一個最根本的相關方就是用戶的實際業務 需求了,用戶可能認爲本身的業務需求都是很明確的,例如電商裏的用戶想查詢本身的交易數據,可是這個業務問題轉移到網站的開發人員和業務人員,面對這麼多 用戶的交易查詢那就是一個超級複雜的計算問題,如是網站的業務和開發人員就會根據本身系統自己的特色和問題,進一步思考用戶業務計算問題的本質,談論業務 計算本質這個問題若是展開細化是很是複雜的,由於現實的業務場景實在是太多太複雜了,可是放到網站實時計算這個角度,其實有一個很簡單的解決思路,咱們回 顧下咱們前面討論的計算瓶頸問題,其實這個問題的本質不是計算可否成功完成的問題,而是計算是否能及時完成的問題,如 果用戶的請求計算的確是無法很快完成,那麼咱們就不要讓用戶以爲這個計算是能很快的完成,這個作法也有一個專有名詞那就是異步計算,可是若是咱們把難以快 速完成的計算都這麼來處理,雖然讓用戶感受網站已經很坦誠的告訴本身能力有限啊,可是苛刻的用戶可不必定會買這個帳,所以當有同類型網站使用新的技術手段 解決了快速實時計算問題後,假如咱們的網站仍是駐步不前,那麼後果就會很嚴重了,那麼這個時候咱們又該如何突破了?

  那麼咱們就得進一步思考計算自己到底哪裏出現了影響速度的問題,計算自己包含三個方面,首先是用於計算的計算資源,再就是作運算的工具即CPU,最後是計算的最終結果, 若是業務計算慢的緣由是由於數據量太大了,CPU很難快速完成,那麼這個時候咱們有一些手段能夠解決這個問題,咱們能夠把海量數據作一個分類,例如存儲系 列裏說的歷史交易數據和當日交易數據的分類,當日數據由於數據量有限在必定條件下能夠快速計算出來,面對歷史數據,若是咱們的計算結果最終是很簡單的並且 在必定時間範圍裏是不會變化的,那麼咱們可不能夠這麼考慮,讓這些結果提早計算出來,而後將結果存儲在效率更高的存儲設備裏例如內存,當用戶請求操做這個 業務計算時候咱們只須要直接讀取緩存裏的計算結果就好了,這樣就避免了計算,同時計算結果存儲在效率高效的緩存裏,用戶得到響應的速度也會快多了,這個其實就是網站靜態化技術裏ESI技術背後的深意了。

  固然當咱們要解決網站性能問題,不太可能單獨從計算或者存儲一個維度來思考,通常都是把雙方放在一塊兒思考,按照我前面提到計算和存儲介質的效率問題, 咱們發現存儲實際上是最容易影響網站效率的痛點,實際狀況也是如此,當網站發生計算瓶頸問題以前,更多的效率問題仍是由存儲所致使的,並且複雜計算過程也是 須要存儲參入才能正常完成,例如計算過程裏的中間結果當超出CPU緩存大 小後咱們就不得不將中間結果放到內存裏,當內存也不夠的時候咱們就得放到硬盤裏,因此解決計算效率問題也受到存儲性能很大的影響。假如咱們仍是按照木桶理 論來理解這個問題,咱們發現無論是單純的存儲問題仍是計算和存儲混合的問題,最終的短板都是其中效率最差的哪一方,而計算和存儲裏效率最差的一方就是網絡 了,不過有些馬虎的朋友可能說如今寬帶好快了,我在網上下載一部幾個G的電影也就幾十秒,甚至有時比我硬盤拷貝還快,像你說網絡是最大的短板其實不許確 的,這位朋友的想法的確有他的道理,可是不是每一個人使用的網絡都是你那麼快呢,並且如今移動互聯網 已經普了及,移動互聯網速度比普通寬帶就差多了,並且你在移動設備上使用網絡流量越大,成本也就越高,若是你認爲我說的這些問題都不算啥,網絡還和地域的 距離有關,你寬帶很快,你想訪問大洋彼岸美國的網站(這個網站在中國沒有任何緩存處理),訪問速度確定仍是快不起來,並且互聯網的連通路徑自己也很複雜, 例如你感受本身訪問的是一個上海本土的網站,可是這個網站說不定好多重要服務器是放置在北京,這麼複雜的網絡環境,這麼多不可控的因素還會影響網絡的傳輸 效率,網絡談何能說本身性能比硬盤強呢?

  由此咱們就能夠發現谷歌和雅虎總結的web前端優化技巧以及我這裏談的網站靜態化技術大部分都是圍繞如何解決網絡傳輸效率來進行了,由於它是整個木桶 最大的短板,咱們只有首先解決了這個短板,那麼再去解決其餘因素的效率問題,才能發揮其做用。這裏的這個解釋也能夠解答前不久一個網友問我,爲何我講網 站優化不多講解如何編寫高效的代碼,而都是從一些和代碼無關的角度來闡述的了,其實你想經過代碼優化提高網站性能,你首先要解決好對網站效率影響更大更關 鍵的要素例如網絡通信問題,不然你代碼優化的再好,對最終效果影響都是有限的。

 

網站靜態化處理—web前端優化—中(12)

Web前端不少優化原則都是從如何提高網絡通信效率的角度提出的,可是這些原則使用的時候仍是有不少陷阱在裏面,若是咱們不能深刻理解這些優化原則 背後所隱藏的技術原理,頗有可能掉進這些陷阱裏,最終沒有達到最佳的預期效果,今天我在這裏分析下瀏覽器和服務端通信的一些細節問題,但願經過分析這些細 節問題,能給你們一個啓迪,能更好的理解這些優化原則背後的隱祕,最終能更好的運用這些原則。

  網站的通信技術是構建在http協議上,http協議底層通信手段使用的是tcp/ip協議,可是tcp通信協議在創建鏈接和斷開鏈接這兩個動做上是 很是消耗通信性能的,這主要是由於tcp/ip協議在鏈接創建時候的三次握手機制和斷開鏈接時候的四次揮手機制所致,咱們來看看下面的圖形:

 

  圖中中間被紅色標記的方塊就是tcp/ip協議在創建鏈接時候須要發送三次報文才能確認鏈接是否創建成功,中間四個藍色的方框就是說明tcp/ip協 議在斷開鏈接時候要發四次報文才能肯定鏈接最終被斷開,而一個具體的http請求和響應也就發送兩次報文,這也就說明若是瀏覽器每次和服務端的交互都要新 建和關閉一個tcp/ip鏈接,那麼瀏覽器和服務器之間就要往返9次報文通信,而真正用來處理用戶請求的報文確只有其中的兩次,換句話說這樣的一個請求大 概會有80%左右的性能都不是用來處理業務需求,等因而損失了80%左右的性能,固然這個比率是9次報文交互的數據大小一致狀況下得出的,若是用戶業務請 求和響應的數據量比較大,那麼創建鏈接和斷開鏈接的性能損失佔比會下降,不過就算佔比下降了那也是在請求處理自己的時間變的更慢的基礎上的下降,要是瀏覽 器和服務器之間的距離特別大,那麼多出來的7次報文交換的效率問題就更加嚴重了,無論怎樣,tcp/ip的三次握手機制和四次揮手機制只要發生都會對網絡 請求效率產生重大影響。

  爲了解決這個報文交互次數過多的問題,http協議自己也發生了改變,那就是http開始採用了長鏈接,使用長鏈接後網站只須要開啓一個長鏈接,在用 戶關閉瀏覽器關閉以前瀏覽器裏的網頁都會複用這個長鏈接。不過http協議的1.0版本默認是不啓用長鏈接的,因此在使用http協議1.0版本時候我就 得手動的打開長鏈接,這個方法就是在http頭裏設置Connection: Keep-Alive,而http1.1版本里長鏈接是默認打開的,因此不須要咱們手動的設置,並且時下的瀏覽器幾乎都支持http1.1協議,所以大多 時候狀況下咱們是沒有必要手動去打開長鏈接的。

  雖然http協議採用長鏈接後能夠減小網站通信時候三次握手和四次揮手的次數,可是長鏈接創建起來後須要瀏覽器和服務器長時間維護,這自己會消耗瀏覽 器和服務器的性能,特別是服務器端長時間維護長鏈接自己還會損壞服務器處理併發的能力,因此早期瀏覽器會限制http1.1開啓鏈接的數量,例如ie7這 個古董瀏覽器,它准許http1.1最多開啓2個長鏈接,而http1.0由於默認使用短鏈接它默承認以開啓4個,下面有張圖能夠說明,以下所示:

 

  提高瀏覽器加載效率的手段除了提高每一個鏈接的傳輸效率外,其實還有一種方式,這個方式就是使用多個鏈接進行並行加載,這個等於幾我的聯合起來一塊兒完成 一個任務,那麼效率確定就比一我的高,而頁面加載時候很符合使用併發加載的場景,例如咱們讓頁面裏的圖片並行加載確定會比一個個加載圖片的效率要高多了。 回到瀏覽器支持的鏈接數的問題,因爲早期瀏覽器在http1.0和http1.1鏈接數的差別,某些網站例如維基百科這樣的網站,它的靜態資源特別多,爲 了充分發揮併發的優點,它將存放這些靜態資源的服務器採用http1.0協議,這樣就能並行加載更多的靜態資源,由於這個並行加載的整體效率提高相比 tcp/ip握手和揮手的損失要高的多,不過如今這個手法已經起不到什麼做用了,由於新版的瀏覽器已經把兩種版本的http協議支持的鏈接數調整一致了, 由於長鏈接能夠複用鏈路,所以使用長鏈接的效率會比非長鏈接更好。

  上面鏈接數也是有一個限制的,這個限制就是必須是在同一個域名下,若是一個頁面某些靜態資源放在不一樣域名下面,那麼這個作法就能夠增長頁面裏的併發數 量,例如咱們把一些不是常常變化的靜態資源例如圖片、外部的css文件以及javascript文件單獨放置在一個靜態資源服務器上,靜態資源服務器對外 的url地址和頁面自己的url地址不在同一個域名下,那麼頁面自己的併發加載鏈接數就會增長一倍,不過這也就意味着瀏覽器端要維護的長鏈接數會變得更 多,雅虎工程師曾經總結過一個頁面裏合理的域名數量,那就是兩個,這個結論的提出已通過去了好多年了,如今的瀏覽器和服務器的性能已經今非昔比了,這個跨 域數量應該能夠增長點,不過我我的認爲一個頁面的裏包含的域名數量仍是不要太多,其實若是咱們web前端優化手段使用得當,兩個不一樣域名就足夠用了,多了 價值不大,除非你網站狀況是在特殊,例如你看看如今瀏覽器自己支持的鏈接數量已經很高了,大部分都是6,ie9甚至還達到了10,翻個倍就有12和20個 鏈接數,咱們在翻個倍就是24和40個,這個數字看起來就很恐怖了,一個計算機支持這麼多併發,假如你在瀏覽器還打開個網站也是這麼幹的,那麼瀏覽器的並 發數多的實在太嚇人了,我估計到時計算機自己就跑不動了,因此10多個鏈接數很夠用了,你合理發揮下這些鏈接數網站的性能就能有很大提高,再說了一個網站 併發鏈接數太多那自己就說明了你在減小http個數這個手段沒有運用好。

  回到web前端優化的手段,咱們若是把這些手段再仔細分析下就會發現不少手段使用都是在同步請求這個場景下進行了,固然這些手段在合適狀況下也能做用 於異步加載場景,可是異步加載場景發生併發加載以前須要一個單線程的異步加載,這個單線程的異步加載就和分佈式系統裏的單點故障有點像了,它頗有多是整 個流程的軟肋所在,因此合理使用同步請求還能讓異步操做性能更加優秀作好準備。上面我講到瀏覽器在同一個域名下最多能夠開啓多少個鏈接數,可是從事web 前端開發的人都能感受到,咱們作頁面開發時候實際上是無法控制這個鏈接數的,那麼問題來了,這麼多鏈接究竟是在什麼條件下被開啓的呢?這個問題很是有意思 的,咱們來看下面的瀑布圖:

 

  從上面的瀑布圖咱們發現,並行下載的是圖片,這個推而廣之要是咱們看見某些網站的網頁作過併發優化處理的設計,咱們就會發現併發的資源都是純靜態的資 源,那麼這個併發鏈接數跟咱們頁面的設計存在一個怎樣的關係呢?首先咱們總結一下頁面裏的靜態資源,在頁面裏靜態資源有html,若是html裏面有內聯 的css代碼和javascript代碼,那麼這些代碼也會歸屬於html,除了html外還有外部的css文件、外部的javascript文件和頁面 裏使用到的圖片,那麼這些要素怎樣會促發頁面的並行加載了,換個說法這些要素又是如何促使瀏覽器同時打開更多鏈接呢?

  首先咱們要明確一個問題,瀏覽器之因此能夠打開更多鏈接數,讓這麼多鏈接並行執行是有個前提的,這個前提就是這些資源是否是被並行加載的,例如像外部 css文件,圖片這樣的資源,這些資源下載完畢後立刻就能夠使用,由於它們下載完畢後沒有邏輯性問題要處理所以下載完畢後就能夠直接拿來使用,所以它們並 行加載不會影響到頁面的展現問題,這個狀況若是碰到javascript就有點麻煩了,外部javascript代碼是包含邏輯在裏面,並且有些邏輯頗有 可能會影響頁面的展現,因此javascript下載完畢後,瀏覽器就得立刻執行,因此咱們就會看到這樣的瀑布圖,以下圖所示:

 

  上面的空白區就是瀏覽器在執行javascript代碼所要花費的時間。瀏覽器開啓多少個鏈接是瀏覽器自發的行爲,這個自發行爲主要出於提高瀏覽器並 發下載效率的角度出發的。因爲如今瀏覽器的鏈接基本都是採起的是http1.1協議,也就是使用的長鏈接,那麼鏈接創建後這個鏈接就會長期維護,若是這個 長鏈接是單獨的靜態資源服務器上的長鏈接,這個問題倒沒什麼,若是這個長鏈接放在主域名下面,問題就來了,主域名在頁面初始化加載時候會用來下載 html,若是咱們爲提升併發下載效率,讓這個主域名下還放置其餘的靜態資源,那麼可能會致使瀏覽器和主域名的服務器下維護更多的長鏈接,而頁面後續操做 基本是使用ajax來操做的,而ajax每每只會複用其中一個長鏈接,那麼其餘多餘的長鏈接等於要空轉了,這個空轉還須要消耗瀏覽器和服務器的系統資源, 因此咱們發現主域名下的請求資源類型必定要認真加以控制,能遷移到單獨的靜態資源服務器上的必定要進行遷移,儘可能讓主域名下處理的請求都是包含業務邏輯的 請求,這樣就能夠有效提高系統資源的使用率。這個問題進一步思考下去,咱們就會發現若是服務端的業務應用服務器以前放置一個反向代理,反向代理都是使用靜 態資源服務器,而靜態資源服務器對併發的承載能力是遠超業務應用服務器,若是主域名下咱們不當心放置了太多靜態資源,要是後臺使用了反向代理,那麼反向代 理也能夠減輕這種長鏈接所形成的計算資源損失。

  上面這些場景都是在瀏覽器同步請求下進行了,那麼換到異步請求這個並行加載靜態資源的手段還有效嗎?回答這個問題前,咱們首先要想一想異步加載會致使新 的靜態資源被加載嗎?這個固然可能,特別是在前端MVC的場景下,咱們會把模板技術放到瀏覽器端完成,這個時候有些html模板一開始可能會包含在 javascript代碼裏,做爲一個變量存儲下來,而這個模板裏頗有可能包含好多新的圖片被使用,當ajax從服務端獲取數據後,解析了這個模板,而後 咱們把構造好的模板加入到頁面的DOM結構裏,瀏覽器從新渲染頁面時候看到不少新圖片須要加載,就有可能會開啓多個鏈接進行並行加載來提高資源加載效率, 若是碰到經過ajax技術動態加載外部CSS文件,那麼這個並行加載狀況就會更加突出了,由於css文件裏頗有可能包含大量的圖片資源,若是咱們把不變的 靜態資源都放置在了單獨的靜態資源服務器,那麼這個並行加載就不會在主域名下打開更多長鏈接,因而可知,將靜態資源使用單獨的域名的靜態資源服務器處理的 好處很是之多。

  如今http2.0協議還在起草之中,http2.0若是落地將會給web前端優化技術產生重大影響,http2.0打算在一個頁面裏只使用一個 tcp/Ip鏈接,不過http2.0會在這個鏈接上進行鏈路複用,也就是讓一個鏈接上也能作到並行操做,讓鏈接的利用率更高,若是http2.0落地 後,web前端裏那些用於減小http鏈接數的手段都會失去市場了,由於協議自己就能處理好併發的問題了,到時像外部css文件,外部 javascript文件,css sprite技術說不定就要成爲歷史了。

 

網站靜態化處理—web前端優化—下【終篇】(13)

本篇繼續web前端優化的討論,開始我先講個我所知道的一個故事,有家大型的企業順應時代發展的潮流開始投身於互聯網行業了,它們爲此專門設立了 一個事業部,不過該企業把這個事業部裏的人事成本,系統運維成本特別是硬件採購的成本都由總公司來承擔,固然互聯網業務上的市場營銷成本這塊仍是由該事業 部本身承擔,但是網站一年運維下來,該公司發現該事業部裏最大的成本竟然不是市場營銷的開銷,而是短信業務和寬帶使用上的開銷,是否是有點讓人感到意外 呢?下面我來分析下這個場景吧。

  短信這塊是和通信運營商有關,很難從根本上解決,固然該企業能夠考慮使用像微信這樣的工具來分攤下短信的成本,可是寬帶流量消耗這個問題卻很難有第二 選擇了,可能有人會感到詫異,一家作互聯網的企業,用戶都是使用本身掏錢的寬帶來上網的,爲啥企業會有寬帶流量的成本呢?其實互聯網公司的後臺服務都是會 放置在IDC即數據中內心的,除非你的企業是真正的高富帥,或者你自己的核心業務就是互聯網業務,這樣的企業纔有可能會自建數據中心,絕大部分企業都會租 用第三方的數據中心,並且有些企業爲了容災還會在不一樣地域創建不一樣的數據中心,不一樣數據中心之間是經過專線來通信的,而專線的成本是很高的,咱們想讓本身 開發的網站讓更多人用,能夠經過改造服務端併發處理能力來達到這個目的,可是這裏還有一個制約因素,那就是服務端使用的帶寬,通常而言,企業選擇多大帶寬 是能夠估算出來,最終採用一個合理的帶寬,可是,若是這家公司是電商類型網站,就頗有可能碰到像雙十一啊,或者自身作大促銷的狀況,這個時候服務端的負載 壓力就會成倍增長,遠遠超出平時的網絡流量,如是企業會提早擴充帶寬,而擴充的帶寬流量是昂貴的,這樣就會無形增長網站運營成本。若是咱們不去思考成本問 題,當今社會講求環保,例如淘寶就說它們網站沒完成一次交易使用的電量能夠煮熟兩個雞蛋,它們網站一天下來消耗的電量至關於中國一個三線城市一天消耗的電 量,那麼若是咱們能節約每次請求消耗的寬帶流量其實也就是在節約能源,因此無論是從成本角度仍是從環保角度提升寬帶的利用率都是有很大的現實意義的。

  Web前端優化裏有一個技巧就是壓縮http請求的數據量,這個技巧不少人都是簡單認爲http請求的數據越小,那麼http處理速度就更快些,不過 我認爲這結論實際上是一個相對的結論,如今的網速是愈來愈快,不少人家裏接入的寬帶已經使用上了光纖,50兆,百兆的寬帶已經飛進了尋常百姓家了,那麼這時 候其實網絡傳輸100kb數據和傳輸300kb數據的效率差別基本能夠忽略不計了,固然並不是每一個人網絡訪問速度都這麼快,例如咱們使用手機的2G網絡上 網,那麼100kb和300kb的傳輸效率仍是會有很大差別的,因此壓縮http請求大小這個手段在客戶端這塊是一種解決短板的技巧,這個短板就是照顧那 些上網速度太慢的人了,而非對人人平等的技術手段,可是這個問題換到服務端就不一樣了,減小http報文的數據大小能夠提高企業對寬帶的利用率,是一種節約 網站運營成本的一個重要手段,所以壓縮http傳輸數據的大小是一個頗有價值的技術手段。

  用來壓縮http請求數據大小的手段不少,例如使用Gzip壓縮http請求,壓縮圖片等等,不過我這裏要特別說明一個手段那就是減小cookie存 儲數據的大小,這是一個經常被忽視的壓縮http請求大小的技術手段。不過cookie技術對不少初學者經常會感到差別,cookie是客戶端的數據,爲 什麼服務端和客戶端都能操做它,難道服務端也會存儲一份cookie的備份嗎?之因此初學者會對cookie使用有疑問,這主要是初學者不太清楚 cookie信息除了保存在瀏覽器端,它還會包含在http報文頭裏的,每一個http請求響應都會帶着cookie信息進行傳遞,因此cookie既能夠 被客戶端操做也能被服務端操做,若是咱們忽視cookie這個特色,再加上咱們濫用cookie,最後cookie被撐滿了,這也就意味每次請求響應的數 據量會增長,而這些信息可能大部分都不會被使用,純粹多餘。而網站在開發和維護時候很容易不自覺的讓cookie變得愈來愈多,愈來愈大,若是咱們一開始 就明確cookie這個特色,提早設計cookie使用規範,那麼就能夠必定程度上規避cookie不合理使用致使的http數據量變大的問題。若是網站 使用了單獨的靜態資源服務器,而且把靜態資源放置在單獨的域名下面,這個時候咱們還要避免給靜態資源域名下使用cookie技術,由於靜態資源基本都不會 有狀態信息,使用cookie只會無謂的增長請求的數據大小。

  網絡是存儲設備裏效率最差的,若是頁面加載時候還有些請求是一個壞請求,例如頁面訪問的某些靜態資源忽然丟了,瀏覽器這個時候會有一個容錯的作法,這 個作法具體是:瀏覽器不能肯定有問題的請求究竟是由於網速慢了仍是找不到,因此瀏覽器會屢次請求這個url,直到瀏覽器認爲這個url的確是有問題沒法訪 問了,瀏覽器纔不去繼續請求了,若是碰到的資源正好是外部javascript文件,那就頗有可能阻塞整個頁面的加載,因此剔除頁面裏的壞請求也是要常常 留心的事情。

  咱們若是再進一步分析下web前端優化的一些手段,就會發現不少優化手段其實都是基於靜態資源來處理的,靜態資源的特色就是在必定時間範圍內不會發生 變化的,並且當用戶請求靜態資源時候,服務端不須要任何計算操做即消耗CPU資源就能把結果返回給客戶端,靜態資源這種不參與計算的特色就可讓靜態資源 和業務應用服務器解耦,所以咱們能夠把靜態資源單獨抽取出來放置在CDN或者是請求效率處理更佳的靜態資源服務器上。和靜態資源相對的動態資源就很難作到 這點,咱們仔細回味下網站後臺整個應用架構,就會發現全部網站都會使用存儲系統即基本都會用數據庫,並且應用服務器和數據庫又是一種緊耦合的關係,由於我 們想消除存儲系統的狀態問題基本是不可能完成的任務,這就讓應用服務器無法作成CDN的形式,所以動態資源處理想使用CDN這種減小距離對網絡通信影響的 手段基本是很麻煩的。我以爲網站靜態化處理實際上是根據web前端優化技術產生的技術,它讓網站靜態化資源和動態資源分離作的更好,因此我說網站靜態化技術 是充分發揮web前端優化手段的重要保證,這也就是我爲何會把web前端優化的內容也要放在網站靜態化處理系列裏的緣由了。

  靜態資源由於在必定時間裏不會發生變化,容易被緩存,並且瀏覽器自己也有緩存機制,那麼若是咱們把靜態資源緩存在瀏覽器端,用戶請求網站就不須要再去 請求網絡資源,這個效率不就更高了嗎?現實狀況的確是如此,可是咱們想讓瀏覽器端充分發揮這個緩存做用其實並不是那麼簡單,由於咱們會碰到以下的問題,具體 以下:

  問題一:網站對瀏覽器的控制是一種被動控制,用戶纔是控制瀏覽器的主動方,用戶的不少行爲都會致使網站對瀏覽器的緩存設計策略失效,若是緩存失效,那麼用戶再去訪問網站時候就得從新請求資源,因此爲了彌補瀏覽器緩存的不可靠性,CDN技術以及靜態資源服務器的使用就派上用場了。

  問題二:瀏覽器緩存網頁部分資源可讓網頁加載的更快,可是要作到這一點以前,咱們首先要明確什麼時候採用,同時採 用何種方式讓客戶端緩存這些能夠被緩存的資源?那麼咱們在知道某個用戶要訪問網站了,咱們提早把須要緩存的資源發送個用戶,讓用戶先緩存下這些資源,這個 作法確定是開國際玩笑了,通常咱們都是在用戶第一次訪問網站時候將能夠緩存的資源緩存起來,這個時候問題又來了,那就是用戶第一次訪問網站時候由於須要緩 存的資源都沒有被緩存,因此所有的資源都要經過網絡請求下載,這個時候就會致使用戶第一次訪問網站頁面的效率不好,有人可能認爲網站又不是設計爲訪問一次 的產品,只要資源被緩存了網頁就會更快的,要是用戶以爲第一次訪問慢了,就先忍忍吧,之後會快的,這個想法又是再開國際玩笑了。就算用戶忍受了第一次訪問 慢的情形,可是若是用戶使用這個網站的時間間隔是很長的,例如某些專業性的網站,它的用戶可能會很長一段時間後再訪問該網站,而過了這段時間後,瀏覽器緩 存的資源頗有可能失效了,這個時候用戶再去訪問又等因而第一次訪問了,那麼咱們這個緩存設計方案基本就是無效了。

  問題二所反映的問題也就代表咱們在如何合理使用瀏覽器緩存這塊上是須要考慮用戶的使用場景的,須要加入一些業務性的策略了,只有這樣瀏覽器緩存方案才能充分發揮其優點。下面我就來談論下瀏覽器端緩存策略設計的問題了。

  首先咱們來看一個場景,用戶第一次訪問網站,訪問的是網站的首頁,咱們按照web前端優化原則設計了網站首頁,特別是使用了一個優化原則就是把css 合併成一個外部css文件,把javascript代碼也合併成一個外部文件,首頁都引入了這兩個外部文件,這種狀況首頁訪問至少會產生三個http請 求,但是網站首頁其實沒有那麼複雜,也就是說首頁使用的css代碼和javascript代碼其實並不太多,若是咱們把這些代碼就放置到頁面內部,那麼首 頁加載就只有一個請求,雖然這會致使這個請求的數據量變大,不過按照我前面說到壓縮http請求數據大小,其實在提高網絡傳輸速度上這個角度是值得商榷 的,可是多個http請求就會致使瀏覽器打開更多鏈接,而每一個鏈接的創建和銷燬倒是十分消耗計算資源的,那麼若是咱們能把三個請求合併成一個請求完成就一 定會讓請求處理的更快,但是這個作法就會致使css和javascript文件無法被緩存,那麼之後想複用它們就麻煩了。碰到這樣的問題咱們又該如何來抉 擇了?最理想的結果就是兩者兼顧,可是要兼顧兩者,那麼頁面就必定要處理這三個http請求了,咱們到底能不能作到兩者兼顧了?答案是確定的,咱們能夠作 到的。咱們仔細的分析下這個場景,就會發現,快速加載頁面和緩存靜態資源在頁面首次訪問這個背景下實際上是兩個不一樣的業務操做,用戶第一次訪問首頁用戶只會 關心頁面是否快速被加載,至於加載靜態資源的行爲以及緩存靜態資源的行爲,用戶是不用關心,所以咱們就能夠拆分這兩個操做,首先是讓頁面快速被加載,等頁 面加載完畢後,咱們在經過異步手段來加載外部的靜態資源,這樣就能夠作到兩者兼顧了,至於如何異步加載靜態資源,我在之前的文章裏講述過,這篇文章就是《探真無阻塞加載javascript腳本技術,咱們會發現不少意想不到的祕密》,不瞭解這個技術的朋友能夠看看本篇文章。

  不過要讓上面的方案發揮做用是有一個大的前置條件的,那就是咱們要判斷出用戶究竟是不是第一次訪問,並且由於外部的css文件和外部的 javascript文件都被咱們合併成了一個文件,這也就是說首頁裏內嵌的css代碼和javascript代碼和外部文件是有一個冗餘的,若是用戶第 二次訪問時候不須要這些操做了,那麼讓首頁保持這個冗餘是否是就沒有這個必要了?特別是javascript代碼,重複的javascript代碼老是讓 人以爲不放心。這兩個問題的核心仍是在於如何判斷用戶是否第一次訪問,判斷用戶的行爲那就是屬於判斷用戶狀態的問題了,用戶的狀態標記在服務端使用的是 session技術,瀏覽器端使用的是cookie技術,而session技術是一個臨時會話存儲技術,所以使用session是無法判斷用戶之前是否訪 問過該網站,因此這裏只能使用cookie技術(若是瀏覽器支持html5,客戶端保存用戶狀態的信息手段就更加多了,不必定非要使用cookie了), 也就是當用戶第一次訪問網站時候,咱們將一些能夠標記用戶是否訪問過網站的狀態信息存儲在cookie裏,那麼用戶再次訪問這個網站時候,http請求就 會把cookie信息傳送給服務端,服務端經過cookie信息斷定用戶是否第一次訪問,這個時候服務端能夠剔除頁面裏內嵌的css代碼和 javascript代碼,同時能夠阻止瀏覽器再異步加載外部css文件和外部javascript文件行爲,這樣用戶再次訪問網站的行爲也不會被用戶第 一次訪問行爲干擾了。

  上面場景裏還有一個優化手段的使用是值得商榷的,那就是咱們把網站全部的css代碼和javascript代碼合併到一個文件裏。這裏我首先來說講把 全部javascript代碼合併成一個文件的問題,一個網站會包含不少不一樣頁面,不一樣的頁面由於業務場景的不一樣,就會致使每一個頁面都有專屬的處理業務邏 輯的javascript代碼,若是咱們簡單的認爲把javascript代碼放置到外部文件就會讓頁面加載的更快,那麼當咱們合併外部文件時候這些和頁 面緊耦合的業務代碼也會合併到一個文件裏,最後就會致使最終的外部javascript文件變得特別大,對於瀏覽器而言,javascript代碼過多也 會影響到頁面的加載效率和javascript的執行效率,並且這個超大的外部javascript文件對於某一個功能頁面而言有太多冗餘的代碼,因此我 們簡單把所有外部javascript文件合併成一個外部javascript文件這個作法其實並非太好,所以到底哪些javascript外部文件應 該被合併這是有所選擇的。並且把某些業務相關的javascript代碼就寫在頁面,和頁面一塊兒被下載可能比咱們單獨使用外部文件的javascript 效率更高,由於單獨的外部javascript文件會增長頁面http請求的個數,那麼咱們在開發時候那些javascript代碼須要內嵌入頁面,那些 javascript代碼要把放在單獨外部文件裏這也是咱們要注意的策略問題。

  咱們毫無原則的把外部css文件和javascript文件合併成少許的外部文件還會影響到網站的運維和瀏覽器的緩存策略,特別是緩存策略的失效機 制,例如網站某個頁面作了css代碼或者javascript代碼的修改,而這些代碼上生產時候要被合併到一個外部的css文件和javascript文 件裏,而這些外部文件又被不少網頁引用,那麼咱們就不得不讓不少無關的網頁也須要刷新瀏覽器緩存,若是這個修改是做用於公共代碼這個問題還好理解,要是這 個代碼是用於營銷活動這個特定場景下,那麼這樣的刷新緩存操做就會顯得很是沒有必要,若是有天營銷活動結束了,咱們是否是還要再刷新下緩存,剔除多餘的代 碼呢?因此如何合併外部的css代碼和javascript代碼咱們仍是要應該根據業務場景進行合理的分組的。

  Web前端的工做是十分繁重的,網站是和終端用戶打交道,這些終端用戶都是網站的需求方,而web前端是處理終端用戶需求的排頭兵,用戶那麼多,需求 那麼多,因此網站的前端頁面會常常的被修改,修改的頁面就要從新發布生產,這個時候咱們就要刷新瀏覽器的緩存了,那如何來刷新頁面的緩存了,方法其實很簡 單就是改變頁面url的參數,通常網站的靜態資源的url後面咱們會專門加上一個版本號參數,例如:

www.cnblogs.com/sharpxiajun/a.css?v=1234556

  咱們只要改變12345這個參數的值就能讓瀏覽器從新從服務端獲取靜態資源,這個時候問題來了,若是外部css文件或javascript文件被不少 頁面引用,那麼咱們就不得不去手動的更改頁面裏引用這些外部文件的版本號,這個操做不免會有遺漏,就算遺漏問題好解決,若是咱們的頁面是使用服務端模板開 發的,那麼就可能致使生產發佈時候重啓生產服務器,爲了靜態資源發佈重啓服務器的確讓人感受有點得不償失。那麼咱們又如何來解決這個問題呢?

  咱們分析下這個問題的本質就是頁面引用外部css文件和javascript文件的行爲其實包含一個動態性,那麼咱們要解決這個問題就是要拆分出這個 動態性,也就是把要變化的版本號這個動態性拆分出來進行單獨處理,通常咱們就會經過模板語言來從新編寫link和script標籤的代碼,例如在jsp技 術裏咱們能夠自定義一個標籤,將版本號做爲參數傳入標籤裏,當項目發佈時候,模板引擎會根據版本參數不一樣從新編譯出link和script標籤,可是這個 作法仍是有問題的,例如jsp頁面技術,要想更改版本號就得重啓服務,因此這個時候咱們把版本號的計算功能作到獨立的緩存裏,當文件改變後咱們經過更改配 置刷新緩存,這樣就能夠不用重啓服務器就能刷新靜態資源的版本號了。若是咱們網站使用了網站靜態化處理,那麼咱們能夠把這個操做遷移到反向代理這邊來作, 把該操做做爲動靜整合的一部分,若是咱們使用了ESI技術,那麼無非就是刷新下ESI對應的緩存便可。這個動態刷新靜態資源版本號的操做在互聯網裏已經很 流行了,可是如今大部分技術都是關注在如何檢測靜態文件是否發生變化上,例如使用md5技術計算文件的md5值啊,或者是修改下文件的名字啊,可是這些手 段使用時候都沒考慮到是否重啓服務器的問題,最終致使設計方案使用起來比較麻煩,我以爲如何檢測文件是否變化很重要,若是方案能實如今檢測變化的基礎上作 到不用重啓服務器就能刷新緩存,這樣才能讓該方案更加完整和實用。

  OK了,終於把網站靜態系列寫完了,後面我將開啓一個新的系列那就是分佈式和SOA,原本我想把分佈式和SOA分紅兩個系列,最近以爲這兩個系列合併 在一塊兒比較好,不過寫新系列前可能須要一段時間準備,最近一直寫博客都沒抽出時間好好看書,應該要花點時間看書好好學習下了。

相關文章
相關標籤/搜索