HTTP请求流程

详解浏览器发起 HTTP 请求的全过程,包括 TCP 连接、缓存策略、重定向机制及 Cookie 状态保持。

浏览器端发起 HTTP 请求流程

1. 构建请求 (Build Request)

首先,浏览器构建请求行信息,构建好后,浏览器准备发起网络请求。 GET /index.html HTTP1.1

2. 查找缓存 (Check Cache)

在真正发起网络请求之前,浏览器会先在浏览器缓存中查询是否有要请求的文件。其中,浏览器缓存是一种在本地保存资源副本,以供下次请求时直接使用的技术。 当浏览器发现请求的资源已经在浏览器缓存中存有副本,它会拦截请求,返回该资源的副本,并直接结束请求,而不会再去源服务器重新下载。这样做的好处有:

  • 缓解服务器端压力,提升性能(获取资源的耗时更短了);
  • 对于网站来说,缓存是实现快速资源加载的重要组成部分。

3. 准备 IP 地址和端口 (DNS Resolution)

因为浏览器使用 HTTP 协议作为应用层协议,用来封装请求的文本信息;并使用 TCP/IP 作传输层协议将它发到网络上,所以在 HTTP 工作开始之前,浏览器需要通过 TCP 与服务器建立连接。也就是说 HTTP 的内容是通过 TCP 的传输数据阶段来实现的 展示完整流程示意图

第一步浏览器会请求 DNS 返回域名对应的 IP。当然浏览器还提供了 DNS 数据缓存服务,如果某个域名已经解析过了,那么浏览器会缓存解析的结果,以供下次查询时直接使用,这样也会减少一次网络请求。拿到 IP 之后,接下来就需要获取端口号了。通常情况下,如果 URL 没有特别指明端口号,那么 HTTP 协议默认是 80 端口。

4. 等待 TCP 队列 (Queueing)

Chrome 有个机制,同一个域名同时最多只能建立 6 个 TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。当然,如果当前请求数量少于 6,会直接进入下一步,建立 TCP 连接。 背景:这是 HTTP/1.1 协议下的浏览器限制,导致了“队头阻塞”问题。 优化:现在的 HTTP/2 协议支持多路复用 (Multiplexing),可以在一个 TCP 连接中并发处理无数个请求,从而突破了这 6 个连接的限制,极大提升了加载效率。

5. 建立 TCP 连接 (TCP Handshake)

排队等待结束之后,终于可以快乐地和服务器握手了,在 HTTP 工作开始之前,浏览器通过 TCP 与服务器建立连接。

6. 发送 HTTP 请求 (Send Request)

一旦建立了 TCP 连接,浏览器就可以和服务器进行通信了。而 HTTP 中的数据正是在这个通信过程中传输的。 HTTP 请求数据格式 展示示意图

服务器端处理 HTTP 请求流程

1. 返回请求 (Server Response)

  • 服务器响应的数据格式 展示示意图
  • 首先服务器会返回响应行,包括协议版本和状态码。
  • 那么一些无法处理或者处理出错的信息,服务器会通过请求行的状态码来告诉浏览器它的处理结果
  • 服务器也会随同响应向浏览器发送响应头。响应头包含了服务器自身的一些信息,比如服务器生成返回数据的时间、返回的数据类型(JSON、HTML、流媒体等类型),以及服务器要在客户端保存的 Cookie 等信息。发送完响应头后,服务器就可以继续发送响应体的数据,通常,响应体就包含了 HTML 的实际内容。

2. 断开连接 (Connection Handling)

通常情况下,一旦服务器向客户端返回了请求数据,它就要关闭 TCP 连接。不过如果浏览器或者服务器在其头信息中加入了:Connection:Keep-Alive ,那么 TCP 连接在发送后将仍然保持打开状态,这样浏览器就可以继续通过同一个 TCP 连接发送请求。保持 TCP 连接可以省去下次请求时需要建立连接的时间,提升资源加载速度

3. 重定向 (Redirect)

  • 当你在浏览器中打开 后,你会发现最终打开的页面地址是 。这两个 URL 之所以不一样,是因为涉及到了一个重定向操作
  • 在控制台输入如下命令:curl -I geekbang.org注意这里输入的参数是-I,和-i不一样,-I表示只需要获取响应头和响应行数据,而不需要获取响应体的数据,最终返回的数据如下图所示: 展示示意图
  • 响应行返回的状态码是 301,状态 301 就是告诉浏览器,我需要重定向到另外一个网址,而需要重定向的网址正是包含在响应头的 Location 字段中,接下来,浏览器获取 Location 字段中的地址,并使用该地址重新导航,这就是一个完整重定向的执行流程。
  • 301 (Moved Permanently):永久重定向。浏览器会缓存这个重定向,下次直接去新地址,不再请求旧地址(SEO 友好)。
  • 302 (Found):临时重定向。浏览器不会缓存重定向关系,每次还是先去旧地址,再被踢到新地址。

1. 为什么很多站点第二次打开速度会很快?

  • 主要原因是第一次加载页面过程中,缓存了一些耗时的数据
  • DNS 缓存和页面资源缓存这两块数据是会被浏览器缓存的。DNS 缓存主要就是在浏览器本地把对应的 IP 和域名关联起来
  1. 强缓存 (本地直接用)
    • 标志:Cache-Control: Max-age=2000 (HTTP/1.1) 或 Expires (HTTP/1.0)。
    • 行为:浏览器直接判断资源未过期,不向服务器发送任何请求,直接从磁盘/内存读取。状态码通常显示为 200 (from memory cache / disk cache)。
  2. 协商缓存 (问问服务器能不能用)
    • 标志:If-None-Match (对应 ETag) 或 If-Modified-Since (对应 Last-Modified)。
    • 行为:强缓存失效后,浏览器发送请求询问服务器。
    • 如果服务器说“没变”:返回 304 Not Modified,浏览器继续用旧的。
    • 如果服务器说“变了”:返回 200 OK 和新内容。
  • 缓存查找流程示意图 展示示意图

  • 从上图的第一次请求可以看出,当服务器返回 HTTP 响应头给浏览器时,浏览器是通过响应头中的 Cache-Control 字段来设置是否缓存该资源。通常,我们还需要为这个资源设置一个缓存过期时长,而这个时长是通过 Cache-Control 中的 Max-age 参数来设置的,比如上图设置的缓存过期时间是 2000 秒。 Cache-Control:Max-age=2000

  • 这也就意味着,在该缓存资源还未过期的情况下, 如果再次请求该资源,会直接返回缓存中的资源给浏览器。但如果缓存过期了,浏览器则会继续发起网络请求,并且在 HTTP 请求头中带上: If-None-Match:"4f80f-13c-3a1xb12a"

  • 服务器收到请求头后,会根据 If-None-Match 的值来判断请求的资源是否有更新。

    • 如果没有更新,就返回 304 状态码,相当于服务器告诉浏览器:“这个缓存可以继续使用,这次就不重复发送数据给你了。”
    • 如果资源有更新,服务器就直接返回最新资源给浏览器。

2. 登录状态是如何保持的?

  • 用户打开登录页面,在登录框里填入用户名和密码,点击确定按钮。点击按钮会触发页面脚本生成用户登录信息,然后调用 POST 方法提交用户登录信息给服务器。
  • 服务器接收到浏览器提交的信息之后,查询后台,验证用户登录信息是否正确,如果正确的话,会生成一段表示用户身份的字符串,并把该字符串写到响应头的 Set-Cookie 字段里,如下所示,然后把响应头发送给浏览器。 Set-Cookie: UID=3431uad;
  • 浏览器在接收到服务器的响应头后,开始解析响应头,如果遇到响应头里含有 Set-Cookie 字段的情况,浏览器就会把这个字段信息保存到本地。比如把UID=3431uad保持到本地。
  • 当用户再次访问时,浏览器会发起 HTTP 请求,但在发起请求之前,浏览器会读取之前保存的 Cookie 数据,并把数据写进请求头里的 Cookie 字段里(如下所示),然后浏览器再将请求头发送给服务器。 Cookie: UID=3431uad;
  • 服务器在收到 HTTP 请求头数据之后,就会查找请求头里面的“Cookie”字段信息,当查找到包含UID=3431uad的信息时,服务器查询后台,并判断该用户是已登录状态,然后生成含有该用户信息的页面数据,并把生成的数据发送给浏览器。
  • 浏览器在接收到该含有当前用户的页面数据后,就可以正确展示用户登录的状态信息了。
  • Cookie 流程图 展示示意图

总结

HTTP 请求流程示意图 HTTP 请求流程示意图

comments powered by Disqus