初識Http快取君

王興欣發表於2017-11-14

前言

二零一七年十一月十三日,就是我開始前端之旅的第n日,我獨在臥室外徘徊,遇見程君,前來問我道,“你可曾為http快取寫了一點什麼沒有?”我說“沒有”,他就正告我,“你還是寫一點罷,http快取應該很高興與你相識,你們可以相互認識多一點”。

可是我實在無話可說。我只覺得所住的並非人間。產品經理不仁,以程式設計師為縐狗。可真的程式設計師,敢於直面慘淡的薪資,敢於正視淋漓的Bug。這是怎樣的哀痛者和幸福者?然而重構又常常為庸人設計,以時間的流駛,來洗滌舊跡,僅使留下洶湧的加班和微漠的悲哀。在這洶湧的加班和微漠的悲哀中,又給人暫得偷生,維持著這似人非人的世界。我不知道這樣的世界何時是一個盡頭!

而我還在這樣的世界裡活著,於是覺得有寫點什麼的必要了。

快取與效能優化息息相關,在知乎上,有一篇回答令我印象深刻大公司裡怎樣開發和部署前端程式碼? 該回答講述了在部署階段如何利用快取提高效能,節約頻寬。

一、相遇

與快取君的相遇是在一場雪夜,當我看見她,我就知道我們會有故事,害羞的我不敢上前去與她交流。馬克步是我的好朋友,本來不是的,自從我見到他在月下西瓜田裡與猹不可描述後,我們便成了好朋友。他對我說“別怕,我來給你創造環境,你專心做自己的事情。但在此之前我先告訴關於快取君的一點小知識”,我高興極了。

  • 200 :正常的交流,每次你說的話都會在腦海裡過一遍,並且找出合適禮貌的答語來回答你。
  • 304 :她知道你想要什麼,但是需要大腦給她的身體一個指令,然後做出相應的動作給予你想要的。(協商快取)
  • from memory cache或者disk cache :本壘打暗示,身體的本能反應,也是最快的。(強快取)

二、交流

const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer().listen(3000);

server.on('request',(req,res) => {
    const url = path.join(__dirname,'static',req.url);
    fs.readFile(url,(err,data) => {
        //馬克步對我說,你別管其他的,每次有靜態請求
        //就會執行這裡的程式碼,讀取檔案然後返回出去。
        //之後會在這裡設定響應頭
        res.end(data);
    })    
});複製程式碼

然而在交流過程中卻令我高興不起來,她老是對我不冷不熱的。


無論我和她對話多少次,她總是慢慢的考慮半天才回答我,這令我很傷心。於是馬克步對我說,“別難過,我幫你設定一個暗號,每次你使用暗號與她交流的話,這會使你們的交流更愉快一些。暗號有兩種哦”

//第一種 以資源內容hash為版本號
server.on('request',(req,res) => {
    const url = path.join(__dirname,'static',req.url);
    fs.readFile(url,(err,data) => {
        //如果之前的請求在響應頭裡返回了Etag 那麼這次請求就可以拿到
        //req.headers['if-none-match'] == 之前響應頭裡的Etag
        if(req.headers['if-none-match'] == 'hello'){
            res.statusCode = 304;
            res.end();
        }else{
        //響應頭裡設定Etag,下次請求的時候,會在請求頭裡加上If-None-Match
            res.setHeader('Etag', 'hello');
            res.end(data);
        }
        res.end(data);
    })    
});複製程式碼

我再次和快取君攀談了起來。

這一次依然如同陌生人一樣,但是這次我拿到了一個暗號,`Etag:hello`,我激動地趕緊使用暗號重複剛才的話。
這一次依然如同陌生人一樣,但是這次我拿到了一個暗號,`Etag:hello`,我激動地趕緊使用暗號重複剛才的話。

呀,快取君對我態度果然變了呀,而且速度也快了很多。
呀,快取君對我態度果然變了呀,而且速度也快了很多。

//第二種 以資源修改時間為版本號
server.on('request',(req,res) => {
    const url = path.join(__dirname,'static',req.url);
    fs.readFile(url,(err,data) => {
        //如果之前的請求在響應頭裡返回了Last-Modifiedtag 那麼這次請求就可以拿到
        //req.headers['if-modified-since'] == 之前響應頭裡的Last-Modified
        if(req.headers['if-modified-since']){
            res.statusCode = 304
            res.end()
        }else{    
            //在響應頭裡設定上次修改時間 必需為國際標準時間
            res.setHeader('Last-Modified', new Date().toUTCString());
            res.end(data);
        }
        res.end(data);
    })    
});複製程式碼

第二天再次攀談,快取君不認識我了。不過沒關係,我拿到了時間暗號
第二天再次攀談,快取君不認識我了。不過沒關係,我拿到了時間暗號

我們像老朋友一樣愉快的交流了起來
我們像老朋友一樣愉快的交流了起來

老姐、我想。。。

馬克步最知我心思,他告訴我說,你若想要非常親密的關係,其實設定一個分手時間就可以了。

server.on('request',(req,res) => {
    const url = path.join(__dirname,'static',req.url);
    fs.readFile(url,(err,data) => {
        //設定快取過期時間
        res.setHeader('Cache-Control','max-age=5');
        res.end(data);
    })    
});複製程式碼

我懷著忐忑的心徐徐向前。

第一次交談沒什麼變化,但是她給了我一個時間
第一次交談沒什麼變化,但是她給了我一個時間

臥槽!瞬間就親密無間了。
臥槽!瞬間就親密無間了。

但是五秒後,又不認識我了(max-age以秒記)
但是五秒後,又不認識我了(max-age以秒記)

馬克步心想:我尋思給你五秒鐘應該夠了呀!

此次經歷

Http快取按強勢程度分為:

  • 強快取 --- 有效期內,直接從本地獲取資源,不需要傳送請求
  • 弱快取 --- 有效期內同上。在有效期外需要傳送請求,如果返回304就繼續用本地快取,返回200則把新版本快取起來,本地版本扔掉。

這次的經歷實在是沒什麼營養。想要了解詳細的話有如下建議:

  1. 大公司裡怎樣開發和部署前端程式碼?
  2. 使用 HTTP 快取:Etag, Last-Modified 與 Cache-Control
  3. 瀏覽器快取機制剖析
  4. MDN HTTP 快取

最好能按照順序閱讀1、2、3,並且在閱讀過程中輔以MDN文件。


其實馬克步並不是他的真名,我曾經詢問過他的真名,馬克步說“我的名字實在是土的很,因為算命先生說我命中缺土,於是在名字中為我平衡”,我想了想馬克步與猹的故事驚訝道:“難道你叫”,馬克步說:“你想得不錯,我叫閨垚

相關文章