HTTP笔记4:缓存控制

缓存的进化阶段

1 无缓存

优点:简单
缺点:每次的请求资源服务器都返回原始文件,浪费带宽

2 有缓存无更新

第一次请求后,服务器发送完整的文件,浏览器将返回的完整文件存到本地缓存,下次再发送同意请求之后,就从本地获取。
优点:节省带宽
缺点:若服务器的文件更新,浏览器本地缓存任然无更新

3 缓存加上更新(GMT时间格式)

服务器在发送问响应的时候,在响应报文加上了额外的字段:过期时间如Expires: Mon,10 Dec 1990 02:25:22GMT,告诉浏览器此次发送的资源什么时候过期,如果过期,需要重新给服务器发送请求。
优点:缓存可控
缺点:控制功能单一,这种格式更改本地时间后就相当于失效了

4 缓存控制

响应头附带额外信息Cache-Control: max-age=300;字段可对应不同的值,使得缓存控制功能强大

  1. Public表示响应可被任何中间节点缓存:如 Browser <-- proxy1 <-- proxy2 <-- Server,中间的proxy可以缓存资源,比如下次再请求同一资源proxy1直接把自己缓存的东西给 Browser 而不再向proxy2要。
  2. Private表示中间节点不允许缓存:对于Browser <-- proxy1 <-- proxy2 <-- Server,proxy 会老老实实把Server 返回的数据发送给proxy1,自己不缓存任何数据。当下次Browser再次请求时proxy会做好请求转发而不是自作主张给自己缓存的数据。
  3. no-cache表示不使用: Cache-Control的缓存控制方式做前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存
    no-store ,真正的不缓存任何东西。浏览器会直接向服务器请求原始文件,并且请求中不附带 Etag 参数(服务器认为是新请求)。
  4. max-age,表示当前资源的有效时间,单位为秒。

缺点:如果资源时间上过期,但是服务器实际并未更新,服务器仍然会发送新的完整资源给浏览器,同样浪费带宽
所以资源过期后,先判断是否真的有更新,没有的话就只返回一个未更改的消息就行了。

5 缓存控制的升级

响应头的格外信息中添加Etag
Etag是 对资源的编码,如果资源在服务端未被修改,这个值就不会变

Cache-Control: max-age=300;
ETag:W/"e-cbxLFQW5zapn79tQwb/g6Q"

时间过期的时候,发送请求给服务器,附带上Etag,服务器接收到请求后,先对Etag进行比对,不相等才发送更新的资源

与 ETag 类似功能是Last-Modified/If-Modified-Since

  • 当资源过期时(max-age超时),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示上次服务器告知的文件修改的时间,
  • web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对
  • 若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(200)
  • 若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 ,告知浏览器继续使用所保存的cache。

nodejs写一个简单的静态服务器

/* 一个最简单的静态服务器 */
/* 访问 http://localhost:8080/index.html */

const http = require('http')
const fs = require('fs')
const path = require('path')


http.createServer((req, res) => {
    console.log(req)
    fs.readFile(path.join(__dirname, req.url), (err, data) => {
        if (err) {
            res.writeHead(404, 'not found')
            res.end('Oh, Not Found')
        } else {
            res.writeHead(200, 'OK')
            res.end(data)
        }
    })
}).listen(8080)
console.log('Visit http://localhost:8080')

在这里插入图片描述

1 expires 与pragma : no-cache (http 1.0)

expires GMT

const http = require('http')
const fs = require('fs')
const path = require('path')


http.createServer((req, res) => {
    let filePath = path.join(__dirname, req.url)
    fs.readFile(filePath, (err, data) => {
        if (err) {
            res.writeHead(404, 'not found')
            res.end('Oh, Not Found')
        } else {
            // 发送文件之前加设置响应头
            // GTM 时间格式

            let date = new Date(Date.now() + 1000 * 15).toGMTString()
            res.setHeader('Expires', date)
            
			// 同时写 no-cache优先级更高
            //res.setHeader('Expires', 'Wed, 23 Jan 2019 07:40:51 GMT')
            //res.setHeader('Pragma', 'no-cache')

            res.writeHead(200, 'OK')
            res.end(data)
        }
    })
}).listen(8080)
console.log('Visit http://localhost:8080')

开启服务器,响应头成功加上GMT字段
在这里插入图片描述
在这里插入图片描述

pragma : no-cache 不保存缓存

            res.setHeader('Pragma', 'no-cache')

每次返回响应之后清除浏览器缓存,不会出现 form-memory或disk这种字段

no-cache的优先级要高

同时设置,仍然执行no-cache

2 Cache-Control

请求报文

  • Cache-Control: max-age=10 //hi,(代理)服务器我想要
  • Cache-Control: no-cache // hi,(代理)服务器,不要给我缓存的东西,我要新鲜完整的内容
  • Cache-Control: no-store // hi,(代理)服务器, 这是机密请求,别缓存数据,给我最新的

响应报文

  • Cache-Control: max-age=10 //hi,浏览器,把这个文件信息保存起来。当再次需要它时,如果是在10秒以内发起的请求则直接使用缓存(200, from memory cache),否则重新发起网络请求找我要(200)
  • Cache-Control: no-cache // hi,浏览器(代理服务器),你可以缓存,但每次需要向我确认一下
  • Cache-Control: no-store // hi,浏览器(代理服务器),这是机密信息,别缓存

max-age=10

const http = require('http')
const fs = require('fs')
const path = require('path')


http.createServer((req, res) => {
    let filePath = path.join(__dirname, req.url)
    fs.readFile(filePath, (err, data) => {
        if (err) {
            res.writeHead(404, 'not found')
            res.end('Oh, Not Found')
        } else {

            // max-age=10 表示发出的资源期限是10s
            res.setHeader('Cache-Control', 'max-age=10') 
            
            res.writeHead(200, 'OK')
            res.end(data)
        }
    })
}).listen(8080)
console.log('Visit http://localhost:8080')

在这里插入图片描述
在这里插入图片描述

  1. 再次请求index时 浏览器会在请求头设置max-age: 0 告诉浏览器不要拿缓存,而是直接请求服务器拿新资源,而其他资源没有这个
  2. 如果浏览器不加这个字段,下次访问,index仍然走本地缓存,页面就无法更新

no-cache 再次请求不会再访问缓存

no-store 告诉浏览器,不再保存到缓存

直接进行浏览器和服务器之间的资源转换
no-cache 实际上是保存在本地缓存里的,只是不访问

3 Last-Modified 字段对缓存的控制

完整步骤:

  1. 第一次请求,服务器获取文件修改时间,并设置在响应头中
  2. 后几次请求,比较新旧文件修改时间,不同则修改,相同则返回状态码304.
const http = require('http')
const fs = require('fs')
const path = require('path')


http.createServer((req, res) => {
    let filePath = path.join(__dirname, req.url)
    fs.readFile(filePath, (err, data) => {
        if (err) {
            res.writeHead(404, 'not found')
            res.end('Oh, Not Found')
        } else {
        
            // 获取文件的修改时间
            let mtime = Date.parse(fs.statSync(filePath).mtime)
            //10秒内,浏览器直接从自己本地拿,10秒后找服务器要。如果没修改,告诉浏览器没修改就行,如果修改了,给浏览器最新的
            res.setHeader('Cache-Control', 'max-age=10')
            // 第一次访问:服务器判断请求头上有没有字段,没有就加上字段Last-Modified 代表修改时间
            if (!req.headers['if-modified-since']) {
                res.setHeader('Last-Modified', new Date(mtime).toGMTString())
                res.writeHead(200, 'OK')
                res.end(data)
            } else { // 后几次访问,
                let oldMtime = Date.parse(req.headers['if-modified-since'])
                if (mtime > oldMtime) { // 判断最新的修改时间大于上一次修改时间,表示这个文件已经被修改过了,要重新发一次新文件
                    res.setHeader('Last-Modified', new Date(mtime).toGMTString())
                    res.writeHead(200, 'OK')
                    res.end(data)
                } else { // 否则就不修改
                    res.writeHead(304)
                    res.end()
                }
            }

        }
    })
}).listen(8080)
console.log('Visit http://localhost:8080')

拆分步骤

1 获取文件修改时间,并设置到响应头
      // 步骤一:设置文件修改时间
       let stat = fs.statSync(filePath)
       console.log(stat.mtime)
       let modifyDate = new Date(Date.parse(stat.mtime)).toGMTString()
       console.log(modifyDate)
       res.setHeader('Cache-control', 'no-cache')
       res.setHeader('Last-Modified', modifyDate)
       res.writeHead(200, 'OK')
       res.end(data)

在这里插入图片描述
现在修改下文件
在这里插入图片描述
文件最后修改时间就更新了
在这里插入图片描述

2 初次请求时设置字段,之后的请求只需对比文件的修改时间
  console.log(req.headers)
    let mtime = Date.parse(fs.statSync(filePath).mtime)
    if (!req.headers['if-modified-since']) {
        res.setHeader('Cache-control', 'no-cache')
        res.setHeader('Last-Modified', new Date(mtime).toGMTString())
        res.writeHead(200, 'OK')
        res.end(data)
    } else {
        let oldMtime = Date.parse(req.headers['if-modified-since'])
        if (mtime > oldMtime) {
            res.setHeader('Last-Modified', new Date(mtime).toGMTString())
            res.writeHead(200, 'OK')
            res.end(data)
        } else {
            res.writeHead(304)
            res.end()
        }
    }

首次请求先关掉缓存,便于测试
在这里插入图片描述
之后的请求,文件并未修改,返回304,并且response里仍然有响应体,这就是从缓存中拿到的
在这里插入图片描述
单独修改css文件之后,只有css返回200,表示已重新请求响应
在这里插入图片描述

存在的问题

  • 缓存全由浏览器控制,因为加了字段 cache-control:no-cache
  • 浏览器无法控制缓存

面试问题
cache-control:no-cache
LastModified: GMT时间格式
服务器完全控制缓存,服务起来判断给不给浏览器更新页面

4 Etag字段对缓存的控制

  1. 第一次请求设置etag字段:字段里存放的是文件的md5值(通过base64展示)
  2. 每次文件修改,对应md5值也将修改
  3. 所以对比每次的md5值相同时,就返回304,不相同才请求到服务器,更新文件,并更换新文件的md5值
const http = require('http')
const fs = require('fs')
const path = require('path')
const crypto = require('crypto')

http.createServer((req, res) => {
    let filePath = path.join(__dirname, req.url)
    fs.readFile(filePath, (err, data) => {
        if (err) {
            res.writeHead(404, 'not found')
            res.end('Oh, Not Found')
        } else {

            console.log(req.headers['if-none-match'])
            let oldEtag = req.headers['if-none-match']
            if (!oldEtag) {
                let md5 = crypto.createHash('md5')
                // 得到内容的md5值,每次文件内容改变之后,md5值一定会改变
                console.log(md5.update(data).digest('base64'))
                res.setHeader('Etag', md5.update(data).digest('base64'))
                res.writeHead(200, 'OK')
                res.end(data)
            } else {
                let newEtag = crypto.createHash('md5').update(data).digest('base64')
                if (oldEtag !== newEtag) {
                	// 更换掉新的tag值
                    res.setHeader('Etag', newEtag)
                    res.writeHead(200, 'OK')
                    res.end(data)
                } else {
                    res.writeHead(304)
                    res.end()
                }
            }
        }
    })
}).listen(8080)
console.log('Visit http://localhost:8080')

在这里插入图片描述
在这里插入图片描述
修改css文件之后
在这里插入图片描述

前端开发者无论后端用什么缓存策略,只需要每次更新时修改文件名就行了,这样每次拿到的就是最新的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值