HTTP-缓存控制

静态服务器(无缓存无更新请求数据)

  • 优点:简单。
  • 缺点:每次请求必须查找返回原始文件,浪费带宽。

有缓存-无更新请求数据(浏览器本地缓存)

  • 优点:节省资源,速度快。
  • 缺点:服务器缓存中的数据变了,浏览器不知道数据是否发生改变。

缓存作用:

缓存是指代理服务器或客户端本地磁盘内保存的资源副本,利用缓存可减少对源服务器的访问,可以节省通信流量和通信时间。

有缓存有更新请求数据

  • 主要原理:请求被响应的时候,响应报文中有一个Expires :Mon,10 Dec 1990 02:25:22GMT(过期时间),再一次进行请求的时候,用本地的时间与过期时间进行比较,如果本地时间小于过期时间,那么从缓存中获取,如果本地时间大于过期时间,重新向服务器发送请求获取,再一次发送新的过期时间。
  • 优点:缓存可控制。
  • 缺点:控制的功能太单一,这种格式的时间和容易写错。

有缓存+更新机制升级版

Cache-Control: max-age=300;

以上代码代表时间间隔,如果再一次的请求在时间间隔300s之内,就在缓存中获取,否则从服务器获取。

  • Cache-Control还有其他值:
    • Public表示响应可被任何中间节点缓存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中间的proxy可以缓存资源,比如下次再请求同一资源proxy1直接把自己缓存的东西给 Browser 而不再向proxy2要。
    • Private表示中间节点不允许缓存,对于Browser <-- proxy1 <-- proxy2 <-- Server,proxy 会老老实实把Server 返回的数据发送给proxy1,自己不缓存任何数据。当下次Browser再次请求时proxy会做好请求转发而不是自作主张给自己缓存的数据。
    • no-cache表示不使用 Cache-Control的缓存控制方式做前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存。不走缓存,响应报文,是服务器发给浏览器的。浏览器在一次发送请求时,发现这个字段,就不会再缓存中获取数据了,而是再一次向服务器发送请求。 缓存只是本地缓存,而不是服务器对应的缓存。报文会缓存,但是不会使用。
    • no-store ,真正的不缓存任何东西。浏览器会直接向服务器请求原始文件,并且请求中不附带 Etag 参数(服务器认为是新请求)。不存,所有的流程都不进行缓存。连报文都不会进行缓存,啥都不缓存。
    • max-age,表示当前资源的有效时间,单位为秒。
  • 优点: 缓存控制功能更强大
  • 缺点: 不够完美,超过时间间隔再向服务器要文件的时候,服务器会再一次发送源文件,但如果文件未被改变,发送源文件太浪费带宽了,只要发送一个文件未被更改的短信息标示就好了。

缓存+更新终极版

服务器返回的文件以及额外信息,其中Etag 是 对请求文件的编码,如果请求文件在服务端未被修改,这个值就不会变。

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

当超过时间间隔的时候,重新发请求获取源文件的时候,在发送请求的时候附带刚刚保存的文件的ETag ( If-None-Match:W/"e-cbxLFQW5zapn79tQwb/g6Q"),之后于ETag进行比较,如果二者相等,则发送个短消息(响应头,不包含图片内容, 304),如果二者不等则发送新文件和新的 ETag,浏览器获取新文件并更新该文件的 Etag。(浏览器的默认行为。)

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

.html不会缓存,.css和图片都会

原因:图片和CSS的请求都是HTML到达浏览器后,浏览器解析发出的,而HTML是直接输入URL解析出来的。报文也存在差异:


HTTP-缓存控制_第1张图片
htmlq请求报文.PNG

HTTP-缓存控制_第2张图片
css报文.PNG
  • .html文件的报文中浏览器自动添加'Cache-Control:max-age=0',也表示.html文件不能使用浏览器内部缓存。
  • 当浏览器检测到状态码是304的时候,就会在本地的缓存中拿数据,做一个展示。调试的时候以为是服务器发过来的,实际上并不是。
  • 勾选开发者工具控制台Disable cache原理:浏览器自动在请求报文上添加:pragma:no-cache。

Expires和Pragma字段对缓存的控制(http1.0版本)

  • Expires
//报文的响应头
res.setHeader('Expires','web, 23 Jan 2019 07:40:51 GMT')

浏览器收到响应报文,看到这个字段就会进行处理,把它和当前浏览器的时间进行比较,如果灭有超过他,则使用浏览器自己缓存的数据,如果超过了,则重新获取。
更好的代码写法

let data = new Date(Date.now() + 1000*5).toGMTString()
res.setHeader('Expires',data)
  • Pragma
//响应头
res.setHeader('Pragma','no-cache')

不要这个缓存。告诉浏览器不要用本地的缓存,要向服务器重新获取数据。

  • 同时存在
    如果这两个字段同时存在,则Pragma的优先级是比较高的。

-Cache-Control-对字段对缓存的控制

res.setHeader('Cache-Control','max-age=10')

表示数据可以使用10秒,在10秒内不需要重新获取数据。可以直接使用浏览器内部缓存的数据。

res.setHeader('Cache-Control','no-cache')

不走缓存,是在响应报文上,是服务器发给浏览器的。下一次浏览器发送请求的时候,都会重新向服务器要数据。

res.setHeader('Cache-Control','no-store')

不存。所有的地方都不进行缓存。

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

Cache-Control: no-cache
LastModified:sXXX

可以进行缓存,但是下一次请求之前,不可以直接在缓存中拿数据,要先问服务器是否可以在本地缓存中中拿,如果服务器返回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')

      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' )

-Etag字段对缓存的控制

Etag就是对文件进行一个处理,值的获取是由自己决定的。需要的文件和这个值进行一个映射,值变了,文件就变了。

   res.setHeader('Etag', md5.update(data).digest('base64'))
//data进行一个计算,得到md5值,当文件内容发生改变的时候,md5值肯定会变。把md5的值用base64展示,值会很短。

当携带Etag字段的报文到达浏览器之后,当浏览器下一次再去请求数据的时候,请求头中就会有一个If-None-Match这个值,会把这个值在发给服务器。会先看一下当前文件的新的值,进行对比。实质就是检测当前文件的完整性。


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 {
      // example1
      // let md5 = crypto.createHash('md5')
      // res.setHeader('Etag', md5.update(data).digest('base64'))
      // res.writeHead(200, 'OK')
      // res.end(data)

      // example2
      console.log(req.headers['if-none-match'])
      let oldEtag = req.headers['if-none-match']
      if(!oldEtag) {
        let md5 = crypto.createHash('md5')
        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) {
          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' )

小结:

  1. 在互联网上,域名通过DNS服务映射到IP地址之后访问目标网站,也就是说,当请求到达服务器时,已经是已IP地址形式访问了。
  2. 代理:是一种具有转发功能的应用程序,它扮演了位于服务器和客户端“中间人”的角色,接收由客户端发送的请求并转发给服务器,同时也接收服务器返回的响应并转发给客户端。代理不会改变请求URI。每次通过代理服务器转发请求或者响应的时候,会追加写入Via首部信息。GET/HTTP/1.1 Via:proxy1
    • 缓存代理(利用缓存技术);
    • 透明代理,不对报文做任何加工的代理叫透明代理。
  3. 网关:是转发其他服务器通信数据的服务器,接收从客户端发来的请求时,它就像自己拥有资源的服务器一样,对请求进行处理。利用网关可以由HTTP请求转化为其他协议通信。
  4. 隧道:是在相隔甚远的客户端和服务器两者之间进行中转,并保持双方通信连接的应用程序。隧道的目的是确保客户端能与服务器进行安全的通信,本身 不会去解析HTTP请求,请求保持原样中转给之后的服务器。隧道会在通信双方断开连接时结束。
  5. 需要兼容HTTP1.0的时候需要使用Expires,不然可以考虑直接使用Cache-Control
  6. 需要处理一秒内多次修改的情况,或者其他Last-Modified处理不了的情况,才使用ETag,否则使用Last-Modified。
  7. 对于所有可缓存资源,需要指定一个Expires或Cache-Control,同时指定Last-Modified或者Etag。
  8. 可以通过标识文件版本名、加长缓存时间的方式来减少304响应。

学习资料参考:HTTP缓存控制小结

你可能感兴趣的:(HTTP-缓存控制)