呢? 在 HTTP 协议规范中,允许客户端在某些特殊情况下直接使用过期缓存,比如校验请求错误时(如无法再次连通服务器),而加上了 must-reva lidate 后,在校验请求错误时,会返回 504 错误码,而不是使用过期缓存。
浏览器再次请求访问服务器中的该资源时,根据请求资源的时间与 Cache-Control 中设置的过期时间大小,计算出该资源是否过期,
- 如果没有过期(且 Cache-Control 没有设置 no-cache 属性和 no-store 属性),则使用该缓存,结束;
- 否则重新请求服务器;
(2)协商缓存
协商缓存指的是当第一次请求后,服务器响应头 Cache-Control 字段属性设置为 no-cache 或者缓存时间过期了,那么浏览器再次请求时就会与服务器进行协商,判断缓存资源是否有效,即资源是否进行了修改更新。
- 如果资源没有更新,那么服务器返回 304 状态码,表明缓存仍然可用,而不需要再次发送资源,减少了服务器的数据传输压力,并更新缓存时间。
- 如果数据有更新,服务器返回 200 状态码,新资源存放在请求体中。
协商缓存可以基于以下两种方式来实现:
第一种(HTTP/1.0 规范):请求头部中的 If-Modified-Since
字段与响应头部中的 Last-Modified
字段:
Last-Modified
:标示这个响应资源的最后修改时间。第一次请求资源后,服务器将在响应头中带上此信息。
If-Modified-Since
:当资源过期了,浏览器再次发起请求的时候带上 Last-Modified 的时间(放在请求头 If-Modified-Since 中),服务器将此时间与被请求资源的最后修改时间进行对比,
- 如果最后修改时间较大,说明资源有被修改过,则返回最新资源和 200 状态码;
- 否则说明资源无新修改,返回 304 状态码。
- 此种方式存在以下问题:
- 基于时间实现,可能会由于时间误差而出现不可靠问题,并且只能精确到秒级,在同一秒内,Last-Modified 无感知。
- 如果某些文件被修改了,但是内容并没有任何变化(比如只是修改时间发生了变化),而 Last-Modified 却改变了,导致文件没法使用缓存。
第二种(HTTP/1.1 规范):请求头部中的 If-None-Match
字段与响应头部中的 ETag
字段:
Etag
:唯一标识响应资源,是一个 hash 值;第一次请求资源后,服务器将在响应头中带上此信息。
If-None-Match
:当资源过期了,浏览器再次向服务器发起请求时,会将请求头 If-None-Match 值设置为 Etag 中的值。服务器将此值与资源的 hash 值进行比对,
- 如果二者相等,则资源没有变化,则返回 304 状态码。
- 如果资源变化了,则返回新资源和 200 状态码。
- 此种方式存在的问题在于计算 Etag 会消耗系统性能,但可以解决第一种方式所存在的问题,推荐使用。
注意 :
- 如果 HTTP 响应头部同时有 Etag 和 Last-Modified 字段的时候,Etag 的优先级更高,也就是先会判断 Etag 是否变化了,如果 Etag 没有变化,然后再看 Last-Modified。
Ctrl + F5
强制刷新,会直接向服务器提取数据。
- 按
F5
刷新或浏览器的刷新按钮,默认加上 Cache-Control:max-age=0,即会走协商缓存。
5. Cookie
HTTP 是一种无状态协议,即其本身不会记忆请求和响应之间的通信状态,那么 Web 服务器就无法判断此请求到底来自于哪个用户,HTTP 协议中并不会保存关于用户的任何信息。这样设计的好处是不需要额外资源保存用户状态信息,减少了服务器的 CPU 及内存资源的消耗。
但是随着 Web 的发展,很多业务需要保存用户状态。
- 比如电商网站需要在用户跳转到其他商品页面时,仍然可以保存用户的登录状态。不然用户每访问一次网站都要重新登录一下,过于繁琐,体验效果就很差。
- 比如短视频网站希望记录用户以前看过的视频,以便之后向其精准化推荐感兴趣的视频。
为了实现保持状态的功能,这就出现了 Cookie。Cookie (服务器给的凭证)类似于我们逛商场时的会员卡(商家给的凭证),记录着我们的身份信息,只要出示了会员卡,商场工作人员就能确定我们的身份。同样的,只要给服务器发送报文时带上了 Cookie,他就知道我们是谁了。
Cookie 中可以包含任意信息,最常见的是包含一个服务器为了进行跟踪而产生的独特的识别码。
举个栗子:
张三在发出第一次请求后,服务器将其状态信息记录下来,比如他的名字、年龄、地址、购物历史等,并通过响应头 Set-Cookie
字段,给予其一个 id=12345 的独特识别码作为 Cookie,那么其再次向服务器发出请求时,浏览器会自动在请求报文中的 Cookie
字段中带上 id=12345,服务器就可以通过这个查询到张三的具体信息,从而实现了保持状态的功能。
Cookie 属性:
max-age
:过期时间有多长(绝对时间,单位:秒)。
- 负数,表示浏览器关闭即失效。默认即为 -1。
- 正数:失效时刻= 创建时刻+ max-age。
- 0:表示 Cookie 立即删除,即 Cookie 直接过期(从而实现使 cookie 失效)。
expires
:过期时间(相对时间)。
secure
:表示这个 Cookie 只会在 https 的时候才会发送。
HttpOnly
:设置后无法通过使用 java script 脚本访问,可以保障安全,防止攻击者盗用用户 Cookie。
domain
:表示该 Cookie 对于哪个域是有效的。 (Cookie 默认是不能直接跨域访问的,但是二级域名是可以共享 cookie 的)
Cookie 的缺点是如果传递的状态信息较多,使得包过大,将会降低网络传输效率。
一般浏览器限制 Cookie 大小为 4KB。
6. HTTP 版本
随着互联网的发展,HTTP 也在不断升级打怪,下面分别介绍一下 HTTP1.1、HTTP/2 以及 HTTP/3 在前一版本的改进之处。
(1)HTTP/1.1 相比 HTTP/1.0 性能上的改进
HTTP/1.1 是目前最常见的 HTTP 版本,其相对于 HTTP/1.0 有以下改进。
① 持久连接
这个在前文中已经提到过,HTTP/1.0 中一个 TCP 连接只能发送一个请求和响应,而 HTTP/1.1 进行了优化,同一个 TCP 连接可以发送多次 HTTP 请求,减少了建立和关闭连接的性能开销。
Web 服务软件一般都会提供 keepalive_timeout
参数,用来指定 HTTP 持久连接的超时时间。比如设置了 HTTP 持久连接的超时时间是 60 秒,Web 服务软件就会启动一个定时器,如果完成某个 HTTP 请求后,在 60 秒内都没有再发起新的请求,就会触发回调函数来释放该连接。
② 管道机制
持久连接虽然可以多个请求复用同一个连接,但是每次都需要等到上一个请求响应完成后,才能发送下一个请求。
管道机制中,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,即相当于同时发出多个请求,因而可以减少整体的响应时间。
虽然客户端可以同时发出多个 HTTP 请求,不用?个个等待响应,但是服务器必须按照接收请求的顺序依次发送对这些管道化请求的响应,以保证客户端能够区分出每次请求的响应内容。这存在下面问题:
如果服务端在处理一个请求时耗时比较长,那么后续请求的