,浏览器看到这个 302 就知道这只是暂时的情况,不会做缓存优化,第二天还会访问原来的地址。
304 Not Modified
:代表上次的文档已经被缓存了,还可以继续使用,即访问缓存。
400 Bad Request
:一个通用差错状态码,表示请求报文中存在语法错误,客户端发生的错误。
401 Unauthorized
:用户未认证。
403 Forbidden
:表示服务器虽然收到了请求,但是拒绝提供服务,常见的原因是为没有访问权限(即用户未授权)。
404 Not Found
:表示请求资源不存在。
500 Internal Server Error
:表示服务器出现错误,可能是出现了一些 Bug 或故障。
502 Bad Gateway
:通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误(可能后端服务器宕机了)。
503 Service Unavailable
:表示服务器暂时处于超负载或者正在进行停机维护,暂时无法处理请求,可以稍后再试。Web 服务器如果限流,就可以给超载的流量直接响应 503 状态码。
(2)响应头
响应头用于向客户端传递一些额外的重要信息,比如响应内容的长度等。
响应头由字段名和字段值构成,二者之间用冒号进行分隔。常见的一些响应头有:
响应头 |
含义 |
Date |
日期时间信息,表示服务器产生并发送响应报文的日期和时间。 |
Server |
表示HTTP服务器应用程序的信息,类似于请求报文中的 User-Agent |
Location |
此字段会配合重定向使用,用于提供重定向后新的 URI。 |
Connection |
设置发送响应之后 TCP 连接是否继续保持的通信选项 |
Cache-Control |
控制缓存的相关信息 |
Content-Type |
服务器返回的响应类型 |
Content-length |
服务器返回的响应长度 |
Content-Encoding |
服务器返回的响应编码 |
Content-Language |
服务器返回的响应语言 |
Last-Modified |
指定响应内容最后的修改时间 |
Expires |
表示资源失效的时间,浏览器会在指定过期时间内使用本地缓存 |
Etag |
用于协商缓存,返回一个摘要值 |
Accept-Ranges |
用于断点续传,指定服务器所支持的内容范围 |
Set-Cookie |
设置状态信息 |
(3)响应空行
响应空行用于表明响应头已经结束。
(4)响应体
响应体用于传送服务器要发给浏览器的正文。
同请求报文的请求体一样,响应体可包含任意的二进制数据。浏览器收到响应报文后,则会将正文加载到内存,然后解析渲染,最后显示页面内容。
3. HTTP 持久连接
客户端发送一系列请求给服务器,如果服务器与客户端对每个请求/响应对都经过一个单独的 TCP 连接发送,则称为非持续连接,也称为短连接;如果经过相同的 TCP 连接发送,则称为持续连接,也称为长连接。
比如打开一个 Web 页面时,假设该页面含有一个 HTML 基础文件和 2 张图片,如果客户端与服务器通过同一个 TCP 连接来获取这 3 个数据,则为持续连接,如果通过建立 3 次不同的 TCP 连接,则为非持续连接。
非持续连接的缺点:
- 每次建立连接需要三次握手过程,导致总的请求响应时间变长。当然也不是绝对的,如果多个连接可以并行请求,总响应时间可能变短,比如 Chrome 浏览器为了提升加载速度,可以同时打开 6 个并行连接,但多个并行连接会加重 Web 服务器负担。
- 必须为每一个请求的对象建立和维护一个全新的连接,而每一个连接都需要客户和服务器分配 TCP 的缓冲区和保持 TCP 变量,使得 Web 服务器存在严重的负担,因为一台 Web 服务器可能同时服务于数以百计不同的客户的请求。
HTTP(1.1 及之后) 默认采用持续连接方式,但也可配置成非持续连接方式。在报文中使用 Connection
字段来表示是否使用持久连接。
- 如果
Connection
字段的值为 keep-alive
,则表明此连接为持久连接,HTTP1.1 及以后可默认不写。
- 如果
Connection
字段的值为 close
,则表明要关闭连接。
注意:持久连接不是永久连接,一般在一个可配置的超时间隔后,如果此连接仍未被使用,HTTP 服务器就会关闭该连接。
4. HTTP 缓存
对于一些短时间内不会产生变化的资源,客户端(浏览器)可以在一次请求后将服务器响应的资源缓存在本地,之后直接读取本地的数据,而不必再重新发送请求。
我们经常会接触到『缓存』这一概念,比如由于内存和 CPU 之间速度差距较大,为了进一步提升电脑性能,于是设计了 L1 缓存、L2 缓存等,让 CPU 先从缓存中取数据,如果取不到,再去内存取。
又比如在后端开发中,由于数据库一般存储在硬盘上,读取速度较慢,于是可能会采用 Redis 等内存数据库作为缓存,先去 Redis 中取数据,如果取不到,再去数据库中取。
再比如在操作系统中,由于页表进行地址转换的速度较慢,于是有了 TLB 快表,当需要进行逻辑地址到物理地址的转换时,先去查询速度更快的 TLB 快表,如果查不到,再去查询页表,此时 TLB 快表就是一种缓存。
缓存的主要目的在于提升查询速度,一般逻辑如图所示。
同样,在 HTTP 设计中也有缓存的概念,主要是为了加快响应速度,HTTP 缓存的实现依赖于请求报文和响应报文中的一些字段,分为强缓存和协商缓存。
(1)强缓存
强缓存指的是在缓存数据未失效的情况下,那么就会直接使用浏览器的缓存数据,不会再向服务器发送任何请求,逻辑类似于前面举的 L1 缓存、Redis、TLB 快表。
具体实现主要是通过 Cache-Control
字段和 Expires
字段。
Cache-Control 是一个相对时间(即多长时间后过期,http1.1 规范),Expires 是一个绝对时间(即在某个时间点过期,http1.0 规范),如果两个字段同时存在,Cache-Control 的优先级更高。
由于服务器端时间和客户端时间可能不同步,存在偏差,这也就是导致了使用 Expires 可能会存在时间误差,因此一般更推荐使用 Cache-Control 来实现强缓存。
以 Cache-Control 为例,强缓存的具体的实现流程如下:
-
当浏览器第一次请求访问服务器资源时,服务器会在响应头中加上 Cache-Control。Cache-Control 中可以设置以下内容。
-
max-age=秒
,表示缓存将于指定毫秒值后过期。比如:cache-control: max-age=31536000
,表示缓存将于 365 天后过期。
-
no-store
,表示不允许缓存(包括强缓存和协商缓存)。
-
no-cache
,表示不使用强缓存,而是使用协商缓存,即使用之前必须要先去服务器端验证是否失效,如果没失效,则再使用缓存,如果失效了,则返回最新数据。等价于max-age=0, must-reva lidate
。
-
must-reva lidate
,表示允许缓存,并且如果缓存不过期的话,先使用缓存,如果缓存过期的话,再去服务器端进行验证缓存是否还有效。
这里很多小伙伴可能会有疑问,即使没有加上 must-reva lidate,有了 max-age 后,缓存过期了不也会去服务器验证吗,加不加 must-reva lidate 有什么区别