浏览器打开页面时,需要向服务器发送请求,请求静态文件(图片,css,js等),这些静态文件通常不会经常修改,客户端可以将这些不经常修改的静态文件存储起来。当客户端再次请求时,可以直接从本地缓存中读取,这样减少了http请求和服务器负担,加快客户端加载网页速度,提高用户体验,那么这个就是客户端缓存的意义了。
Http 缓存机制作为 web 性能优化的重要手段,很多同学仅仅只是知道浏览器会对请求的静态文件进行缓存,但是为什么被缓存,缓存是怎样生效的,却并不是很清楚。我们首先了解http的传输原理,然后讲解http缓存机制。
HTTP想要发送一条报文的时候,需要经过以下两个步骤:
- TCP三次握手建立起连接管道,HTTP报文会以流的形式通过该管道按顺序传输;
- TCP会将这些数据分别切割成数据块,并且封装在IP分组中,通过IP去传输;
使用TCP作为传输层:
- 传输可靠
- 有序
一个HTTP请求过程如下图所示:
a. 浏览器在发起请求时首先会做什么?答,查找本地缓存
Expires:本地缓存目录中,文件过期的时间(由服务器指定具体的时间)
Cache-control:本地缓存目录中,文件过期的时间(由服务器指定过期的间隔时间,由于浏览器根据间隔生成具体的时间)
Last-Modified:服务器上文件的最后修改时间
Etag:文件标识
b. 浏览器查找完缓存之后接下来要做的是什么呢?答,DNS query
首先客户机将域名查询请求发送到本地DNS服务器,本地DNS服务器先在之前的记录(缓存)中查找,如果有缓存,则直接利用缓存进行解析,如果没有缓存,则进入本地的缓存的寻找。如果本地服务器不能在本地找到缓存,则将请求发送到根域名DNS服务器。
DNS服务器在拿到我们的域名后,会将它解析成IP地址返回给浏览器,这样浏览器就能直接定位到要请求的服务器的地址了。
c. 找到服务器地址之后肯定就是向服务器发送请求,建立连接,这个不用问了。
这样根据IP和端口,浏览器就开始跟服务器建立TCP连接啦。看到TCP连接,我们就想到了三次握手。
TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换 TCP窗口大小信息。
第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
完成了三次握手,客户端和服务器端就可以开始传送数据。
d. 浏览器在接收到服务器返回的数据后会怎么做呢?答,解析数据,生成渲染树
渲染展现页面是肯定的啦,关键是如何渲染。我们先来看一下浏览器的简单处理步骤:
(1)解析HTML/SVG/XHTML,build DOM树。
(2)解析CSS,build CSSOM树。
(3)build render树。render树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在render树中了。
(4)reflow。计算每个Element在设备中显示的具体位置。
(5)paint。最后通过调用操作系统Native GUI的API绘制。
我们先来看看RFC2616规定的47种http报文首部字段中与缓存相关的字段,事先了解一下能让咱在心里有个底:
HTTP缓存有多种规则,根据是否需要重新向服务器发起请求来分类,我将其分为两大类(强制缓存,对比缓存)
在详细介绍这两种规则之前,先通过时序图的方式,让大家对这两种规则有个简单了解。
已存在缓存数据时,仅基于强制缓存(Cache-control/Expires),请求数据的流程如下:
已存在缓存数据时,仅基于对比缓存(Etag/Last-Modified),请求数据的流程如下:
我们可以看到两类缓存规则的不同,强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互。
两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则。
Cache-control优先级高于Expires,Etag优先级高于Last-Modified
a. 浏览器url回车、页面链接跳转、新开窗口、前进、后退
浏览器发现缓存中有这个文件,就不发送任何请求了,直接去缓存中获取展现。(最快)
b. 按下F5刷新
F5就是告诉浏览器,别偷懒,好歹去服务器看看这个文件是否有过期了。于是浏览器就胆胆襟襟的发送一个请求带上If-Modify-since。
然后服务器发现:诶,这个文件我在这个时间后还没修改过,不需要给你任何信息了,返回304就行了。于是浏览器获取到304后就去缓存中欢欢喜喜获取资源了。
c. 按下Ctrl+F5
这个可是要命了,告诉浏览器,你先把你缓存中的这个文件给我删了,然后再去服务器请求个完整的资源文件下来。于是客户端就完成了强行更新的操作。
网上已经有很多关于Cache-control、Expires、Etag、Last-Modified的经典讲解和实例演示,此处我就不再一一描述,大家参考以下链接:
浅谈浏览器http的缓存机制
彻底弄懂HTTP缓存机制及原理
浏览器的日常生活
你需要知道的HTTP常识
简析TCP的三次握手与四次分手
浏览器 HTTP 协议缓存机制详解
HTTP缓存机制详解