關於大型網站技術演進的思考(二十一)--網站靜態化處理—web前端優化—下【終篇】(13)

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

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

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

  用來壓縮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文件,那就頗有可能阻塞整個頁面的加載,因此剔除頁面裏的壞請求也是要常常留心的事情。html5

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

  靜態資源由於在必定時間裏不會發生變化,容易被緩存,並且瀏覽器自己也有緩存機制,那麼若是咱們把靜態資源緩存在瀏覽器端,用戶請求網站就不須要再去請求網絡資源,這個效率不就更高了嗎?現實狀況的確是如此,可是咱們想讓瀏覽器端充分發揮這個緩存做用其實並不是那麼簡單,由於咱們會碰到以下的問題,具體以下: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分紅兩個系列,最近以爲這兩個系列合併在一塊兒比較好,不過寫新系列前可能須要一段時間準備,最近一直寫博客都沒抽出時間好好看書,應該要花點時間看書好好學習下了。

  今天週五了,我是歌手立刻要開始,要準備看電視了,最後仍是按照慣例祝你們晚安,生活愉快啦。

相關文章
相關標籤/搜索