资讯专栏INFORMATION COLUMN

详谈HTTP的缓存机制与原理

Jason_Geng / 447人阅读

摘要:对于的缓存机制来说,策略体现在的头部信息的字段上,而这些策略根据是否需要重新向服务器端发起请求可以分为强缓存和协商缓存两大类。接下来就是要介绍,两类缓存相关相关字段的控制实现了。

概述

缓存的重要性不言而喻,通过网络请求资源缓慢并且降低了客户端的用户体验,增添了服务端的负担。很多短期之内不会经常发生变化的资源文件没必要每次访问都想服务端进行数据请求,而缓存策略的使用就是为了改善客户端的呈现时间,降低服务端的负担。

对于HTTP的缓存机制来说,策略体现在HTTP的头部信息的字段上,而这些策略根据是否需要重新向服务器端发起请求可以分为强缓存协商缓存两大类。接下来用UML时序图的形式来呈现这两大类缓存策略的大体过程。

Tips: Vscode 配合插件 plantUML画(写)UML图很爽。相比之前用的ProcessOn拖拽的形式,你只需要熟悉plantUML的语法,在你的电脑上安装一下java,Graphviz 的环境,不用操心样式的展现UML真心舒服。下面的图我就是用这个工具去画的,很推荐。

强缓存

强缓存紧密联系着一个缓存时间期限,当浏览器请求资源的时候会查看缓存中的资源是否存在并且确定该缓存的资源是否过了“保质期”,若没有超过保质期则将取得缓存中的资源进行下一步处理

协商缓存

可见协商缓存无论如何都会和服务器交互,比较强缓存稍微要复杂一点,但是二者是相辅相成并且可以共同存在的,强缓存优先级较高,意味着请求一个资源时会先比较强缓存的字段,如果命中则不会再执行接下来的协商缓存的过程。

接下来就是要介绍,两类缓存相关HTTP header相关字段的控制实现了。

细节 1.强缓存

与强缓存相关的HTTP header 的字段有两个 Expires以及Cache-Control

Expires

expires 字段规定了缓存的资源的过期时间,在此时间之前,缓存中的资源都是有效的,该字段的 value 是一个格林威治时间格式(GMT)的时间,即世界标准时间,js 通过 new Date().toUTCString()可得到,形如 Tue, 27 Feb 2018 06:37:48 GMT。他的缺点很明显,时间期限是服务器生成,存在着客户端和服务器的时间误差,固定时间,HTTP 1.0时的规范。相比较接下来介绍的cache-control优先级较低。

cache-control

该字段的值(默认为private):

其中最常用的值max-age单位为秒,对比expires体现着一个相对时间,即多少秒后这个强缓存机制下的缓存资源失效。

publicprivate区别在于是否有中间商赚差价(是否允许CDN代理服务器缓存)

需要注意的一点是该字段值定义为no-cache并不是说,不准使用缓存,而是需要走接下来的优先级相对较低的另一类--协商缓存。真正决定不用缓存内的资源是将该值定义为no-store

2.协商缓存

协商缓存是通过客户端和服务端进行HTTP通信时,所在响应头和请求头中互相表达“暧昧”的,相互通气,互送缓存标识。

Last-Modified 和 If-Modified-Since

第一次请求某一个资源时,由于一定不会走缓存,所以服务器端会在资源的响应头中加上一个形如Last-Modified:Mon, 26 Feb 2018 06:37:41 GMT的字段告诉客户端浏览器,这个资源上次最后修改的时间;刷新页面再次请求,这时候的协商缓存会在请求头中加上一个形如If-Modified-Since:Mon, 26 Feb 2018 06:37:41 GMT,字面翻译就是,是否在上个“暧昧”时间后修改了,值毫无疑问是服务端上一次响应给他的时间,让服务器去判断是否在此时间之后资源内容发生了变化

整个过程也很简单,最后的结果也很简短。如果服务端发现改变了资源,就伴着200的 statuscode 和新鲜的资源给到客户端,若是没有修改,304 Not Modified让客户端从缓存中取。

Etag 和 If-None-Match

同样,第一次客户端请求一个资源文件时,服务端随资源在响应头部中甩来一个字段 Etag ,形如ETag:W/"1823823287"该字段的值是该资源在服务器端的唯一标识,生成的Etag值的策略有服务端决定,总之是资源的一个唯一的标识。资源发生变化则该值也发生变化。下一次客户端请求同一个资源的时候,在请求头将这次得到的值放在请求头中一个叫 If-None-Match 的字段中甩给服务端。

整个过程也很简单,最后的结果也很简短。如果服务端发现改变了资源,就伴着200的 statuscode 和新鲜的资源给到客户端,若是没有修改,304 Not Modified让客户端从缓存中取。

上述两个方式中,Etag 和 If-None-Match的优先级要高于Last-Modified 和 If-Modified-Since,进而会衍生出一个思考,二者相比功能相同,但是表达形式决定了 Etag 解决了 Last-Modified 存在的一些问题,比如Last-Modified 是比较时间,精确到秒,若是毫秒级的改变则没法兼顾,存在着周期性更改的资源,然而有可能资源本身的内容并没有改变,那如果重新请求响应意义并不是那么的大。所以不难理解Etag具有高优先级有他的合理之处。

简单操练一下

使用Node的Express框架能够轻松的观察到这些字段带来的影响实验。接下来通过使用Express4.x依旧保留下来的中间件express.static搭建一个建议的静态文件响应服务器。

其中 public 文件夹里面放着一个主页面和两张不一样的图片用来改变

//app.js
const express = require("express")
const path = require("path")

const app = express()

app.use(express.static(path.join(__dirname, "public"), {
    etag: false,
    lastModified:false, 
    cacheControl: false,  
    setHeaders:function(res,path,stat) {
        res.set({
            expires: new Date(Date.now() + 60000),
        })
    }
}))

app.listen(3000, () => {
    console.log("App listening on port 3000!");
});
//index.html



    
    
    
    Document


    

Hello World

详细的express.static的细节请参考文档,这里不过多赘述。

之前我们提到过,expires这个选项是优先级相比较其他的控制选项,所以想要种 expires 观察效果是记得向上述app.js一样把其他的都关掉,因为他们都是默认为 true的。

然后启动服务器,访问3000端口,打开开发人员工具,你就会看到如下被种了expires的响应头了。

之后,你改变图片的名称,调换两张图片的名字,你就会发现在 expires 的时间到之前图片都不会发生变化,并且可见,他是从缓存中取得

接下来,改变app.js

app.use(express.static(path.join(__dirname, "public"), {
    etag: false,
    maxAge:30000,
    lastModified:false,   
    setHeaders:function(res,path,stat) {
        res.set({
            expires: new Date(Date.now() + 600000),
        })
    }
}))

maxAge的单位是毫秒,此时由于优先级的关系,覆盖掉 expires 之后在30秒之内交换图片名称,重新刷新浏览器将不会看到资源发生变化,知道maxAge的时间到。对应的响应头如下:

继续修改app.js:

app.use(express.static(path.join(__dirname, "public"), {
    etag: false,
    maxAge:30000,
    cacheControl: false,
    lastModified:true,   
}))

你需要删掉对强缓存的设置,因为强缓存的设置会比协商缓存被先执行,同样的操作你将看到接下来的响应头和请求头。

随后,你将上述优先级最高的 Etag 字段改为 true,得到的响应头和请求头信息如下:

此外你也会发现如下情况:

这种现象就解释了,协商缓存虽然总是要访问服务器,但是当资源没有变动的时候,服务端只会返回带着304状态码的很小的一个头部,并不会像第一次携带文件资源那么大了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92999.html

相关文章

  • Bitmap优化详谈

    摘要:上面的代码没用过因为我是固定来取样的数据,为什么这个压缩方法叫采样率压缩是因为配合,先获取图片的宽高这个过程就是取样。 目录介绍 01.如何计算Bitmap占用内存 1.1 如何计算占用内存 1.2 上面方法计算内存对吗 1.3 一个像素占用多大内存 02.Bitmap常见四种颜色格式 2.1 什么是bitmap 2.2 Android常见是那种 2.3 常见四种颜色格式...

    jone5679 评论0 收藏0
  • 详谈 Filter 过滤器

    摘要:元素用于指定过滤器的完整的限定类名。除此之外,过滤器不会被调用。参数用于访问后续过滤器。还可以为指定目标资源为某个,例如当用户访问时,会执行名字为的,这时会执行过滤器。防止中文乱码过滤器项目使用框架时。 文章首发在CSDN博客,转载请务必注明以下所有链接,否则考虑法律追究责任。 CSDN地址:http://blog.csdn.net/tzs_1041218129/article/det...

    wind5o 评论0 收藏0
  • JavaScript数据类型----原始类型引用类型(object)详谈

    摘要:做个小小的延伸在这个栗子中,两个对象都有一条相同的值,但这两个对象却不相等,为什么呢因为两个对象分别引用的是存放在堆内存中的个不同的对象,故变量和的值引用地址也是不一样的。 JavaScript中,有七种内置类型:6种原始类型和引用类型,他们分别是: 6种原始类型(基本类型): 空值(null) 未定义(undefined) 布尔值(boolean) 数字(number) 字符串(s...

    tomlingtm 评论0 收藏0
  • 后端技术精选 - 收藏集 - 掘金

    摘要:使用签署免费证书后端掘金本文操作在操作系统下完成,需要和超文本传输安全协议英语,缩写,常称为,红黑树深入剖析及实现后端掘金红黑树是平衡二叉查找树的一种。 使用 Lets Encrypt 签署免费 Https 证书 - 后端 - 掘金 本文操作在Linux操作系统下完成,需要Python和Nginx 超文本传输安全协议(英语:Hypertext Transfer Protocol Sec...

    Meils 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<