[實戰驗證] http緩存(無代理服務器)

一. 背景

最近看了很多關於客戶端緩存機制的文章,大概弄明白了整個原理,可是對於中間一些細節一直有點迷糊,下面的內容是本身作的驗證javascript


二. 準備工做

http緩存的基本狀況能夠看看下這些文章

前端之瀏覽器緩存,一次搞定html

前端也要懂Http緩存機制前端

常見的影響緩存的配置有如下幾個:

  1. 原服務器的Expires
  2. 原服務器的Cache-Control
  3. 代理服務器(nginx)Expires
  4. 代理服務器(nginx)proxy_cache_valid
  5. 代理服務器(nginx)proxy_cache_path中的inactive

當前這篇會先驗證無代理服務器的狀況,有代理的狀況放在以後驗證java

配置說明

原服務器的Cache-Controlnode

名稱 說明
private 客戶端能夠緩存,代理服務器不能夠緩存
public 客戶端和代理服務器均可以緩存
max-age=t 緩存內容將在t秒後失效
no-cache 使用協商緩
no-store 不使用緩存

原服務器的Expiresnginx

這是http 1.0的屬性,如今應該用的少了;該屬性設置的是一個過時時間,過時時間內命中強緩存;過時時間外,協商緩存chrome


三. 驗證

如下是我準備驗證的問題:express

  1. 原服務器的Cache-Control不一樣屬性的實際狀況
  2. 原服務器的Expires的實際狀況
  3. 不使用代理服務器的狀況下,原服務器的ExpiresCache-Control同時存在,那個優先級高

[1-24 更新] 增長一個驗證:json

  1. 若是上述頭都未設置,緩存狀況是怎樣的?

1. 首先是無代理服務器的狀況

驗證方式是使用node的express啓動一個服務後端

const fs = require('fs');
const path = require('path');
const express = require('express');
const app = express();
const port = 3030;

app.use(express.static(path.resolve(__dirname, './')));

app.get('/index', (req, res) => {
  const html = fs.readFileSync(path.resolve(__dirname, './index.html'), 'utf-8');
  res.send(html)
})

// cache驗證
app.all('*', (req, res, next) => {
  // res.header('Cache-Control', 'private');
  // res.header('Cache-Control', 'public');
  res.header('Cache-Control', 'max-age=20');
  // res.header('Cache-Control', 'no-cache');
  // res.header('Cache-Control', 'no-store');
  next();
})

const questions = [
  {
    id: '000',
    name: 'Rose'
  },
  {
    id: '111',
    name: 'Jack'
  }
]

app.get('/api/data', (req, res) => {
  res.status(200);
  res.json(questions);
})

// 監聽端口
app.listen(port, () => {
  console.log(`success listen at ${port}`);
})

複製代碼

1.1 Cache-Control的各屬性驗證

ps:由於不存在代理服務器,因此public和private的區別如今是看不出來的,咱們以後和有代理的狀況一塊兒驗證

// 單獨驗證Cache-Control
app.all('*', (req, res, next) => {
  res.header('Cache-Control', 'max-age=30');
  // res.header('Cache-Control', 'no-cache');
  // res.header('Cache-Control', 'no-store');
  next();
})
複製代碼

如下驗證能夠得出結論:

  1. max-age未過時 -> 命中強緩存
  2. max-age過時 -> 資源未修改 -> 命中協商緩存
  3. max-age過時 -> 資源已修改 -> 服務器獲取資源
  4. no-cache -> 資源未修改 -> 命中協商緩存
  5. no-cache -> 資源已修改 -> 服務器獲取資源
  6. no-store -> 服務器獲取資源
Cache-Control 請求延遲時間 資源是否改變 If-None-Match(request) ETag(respanse) Status Code 結論
max-age=30 10s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 200 OK (from disk cache) 強緩存未過時,命中強緩存
max-age=30 60s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" 200 OK 強緩存過時,資源被修改,從新從服務器獲取資源
max-age=30 10s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 200 OK (from disk cache) 強緩存未過時,命中強緩存
max-age=30 60s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 304 Not Modified 強緩存過時,資源未修改,命中協商緩存
no-cache 30s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" 200 OK 資源被修改,未命中協商緩存,從服務器獲取資源
no-cache 30s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 304 Not Modified 資源未修改,命中協商緩存
no-store 30s W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" 200 OK 不使用緩存,直接從服務端獲取資源
no-store 30s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 200 OK 不使用緩存,直接從服務端獲取資源

1.2 Expires驗證

ps1:若是這個Expires字段後端沒有處理過的話,返回的應該是GMT的標準時間Wed, 23 Jan 2019 09:52:55 GMT,也就是格林威治的標準時間;而咱們通常使用的是本地時間Wed Jan 23 2019 18:06:15 GMT+0800 (中國標準時間),因此要作一下適當的處理

ps2:我是在chrome上用localhost上測試的,但不知道爲何設置Expires後,無論過沒過時,無論有沒有同時設置Cache-ControlStatus Code狀態一直是304的,據說好像是由於用了localhost的關係,這個與線上並不必定徹底一致;這裏關於Expires的測試我是加了nginx代理了以後的結果,不過代理的緩存沒有設置

ps3:Expires的結果就僅供參考吧,以上

const moment = require('moment');
// cache驗證
app.all('*', (req, res, next) => {
  res.header('Expires', getGLNZ());
  next();
})
// 轉換格林威治時間
function getGLNZ() {
  return moment().utc().add(30, 's').format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT';
}
複製代碼

如下驗證能夠得出結論:

  1. Expires未過時 -> 命中強緩存
  2. Expires過時 -> 資源未修改 -> 命中協商緩存
  3. Expires過時 -> 資源已修改 -> 服務器獲取資源
Expires 請求延遲時間 資源是否改變 If-None-Match(request) ETag(respanse) Status Code 結論
當前時間+30s 10s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 200 OK (from disk cache) 強緩存未過時,命中強緩存
當前時間+30s 60s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 304 Not Modified 強緩存過時,資源未修改,命中協商緩存
當前時間+30s 10s W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" 200 OK (from disk cache) 強緩存未過時,命中強緩存
當前時間+30s 60s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" 200 OK 強緩存過時,資源被修改,從新從服務器獲取資源

1.3 Expires與Cache-Control優先級

結論:Cache-Control優先級比Expires優先級高

const moment = require('moment');
// cache驗證
app.all('*', (req, res, next) => {
  res.header('Cache-Control', 'max-age=60');
  // res.header('Cache-Control', 'no-cache');
  // res.header('Cache-Control', 'no-store');
  res.header('Expires', getGLNZ());
  next();
})

function getGLNZ(){
  return moment().utc().add(30, 's').format('ddd, DD MMM YYYY HH:mm:ss') + ' GMT';
}
複製代碼

如下驗證能夠看出,在Cache-ControlExpires同時設置的狀況下,Expires是失效的

Expires Cache-Control 請求延遲時間 資源是否改變 If-None-Match(request) ETag(respanse) Status Code 結論
當前時間+30s max-age=60 10s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 200 OK (from disk cache) Expires與Cache-Control都未過時,命中強緩存
當前時間+30s max-age=60 40s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 200 OK (from disk cache) Expires過時,Cache-Control未過時,命中強緩存
當前時間+30s max-age=60 100s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 304 Not Modified Expires與Cache-Control都過時,資源未修改,命中協商緩存
當前時間+30s no-cache 10s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 304 Not Modified Expires未過時,可是命中協商緩存
當前時間+30s no-cache 60s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 304 Not Modified Expires過時,命中協商緩存
當前時間+30s no-store 10s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 200 OK Expires未過時,直接獲取服務端資源
當前時間+30s no-store 60s W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 200 OK Expires過時,直接獲取服務端資源

1.4 Expires與Cache-Control都未設置

如下驗證結論:

  1. If-None-MatchETag相同 -> 協商緩存
  2. If-None-MatchETag不相同 -> 服務器獲取資源
資源是否改變 If-None-Match(request) ETag(respanse) Status Code 結論
W/"3a-LT60UfEg/Jmv4cmkNOAvZSUh6Qo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 200 OK 服務器獲取資源
W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" W/"37-W4vHvR7+YshQyC9DoyT14egVUqo" 304 Not Modified 命中協商緩存

四. 總結:

1. 只設置Cache-Control

  • max-age未過時 -> 命中強緩存
  • max-age過時 -> 資源未修改 -> 命中協商緩存
  • max-age過時 -> 資源已修改 -> 服務器獲取資源
  • no-cache -> 資源未修改 -> 命中協商緩存
  • no-cache -> 資源已修改 -> 服務器獲取資源
  • no-store -> 服務器獲取資源

2. 只設置Expires

  • Expires未過時 -> 命中強緩存
  • Expires過時 -> 資源未修改 -> 命中協商緩存
  • Expires過時 -> 資源已修改 -> 服務器獲取資源

3. Cache-ControlExpires同時存在

  • 只有Cache-Control生效

4. Cache-ControlExpires都未設置

  • 對比request中的If-None-Matchresponse中的ETag
  • If-None-MatchETag相同 -> 協商緩存
  • If-None-MatchETag不相同 -> 服務器獲取資源
  • 感受實際上就是走的Cache-Control no-cahce

以上,驗證了無代理服務器的狀況下,http緩存經常使用的兩個配置的結果;下一篇驗證加了代理服務器的狀況

相關文章
相關標籤/搜索