转载:https://www.shangmayuan.com/a/0648d9157e584bf7931bddd6.html

 

曾经有这么一道面试题:从 URL 在浏览器被被输入到页面展示的过程当中发生了什么?

相信大多数准备过的同窗都能回答出来,可是若是继续问:收到的 HTML 若是包含几十个图片标签,这些图片是以什么方式、什么顺序、创建了多少链接、使用什么协议被下载下来的呢?github

 

要搞懂这个问题,咱们须要先解决下面五个问题:web

  1. 现代浏览器在与服务器创建了一个 TCP 链接后是否会在一个 HTTP 请求完成后断开?什么状况下会断开?面试

  2. 一个 TCP 链接能够对应几个 HTTP 请求?chrome

  3. 一个 TCP 链接中 HTTP 请求发送能够一块儿发送么(好比一块儿发三个请求,再三个响应一块儿接收)?浏览器

  4. 为何有的时候刷新页面不须要从新创建 SSL 链接?服务器

  5. 浏览器对同一 Host 创建 TCP 链接到数量有没有限制?并发

第一个问题

现代浏览器在与服务器创建了一个 TCP 链接后是否会在一个 HTTP 请求完成后断开?什么状况下会断开?app

在 HTTP/1.0 中,一个服务器在发送完一个 HTTP 响应后,会断开 TCP 连接。可是这样每次请求都会从新创建和断开 TCP 链接,代价过大。因此虽然标准中没有设定,某些服务器对 Connection: keep-alive 的 Header 进行了支持。意思是说,完成这个 HTTP 请求以后,不要断开 HTTP 请求使用的 TCP 链接。这样的好处是链接能够被从新使用,以后发送 HTTP 请求的时候不须要从新创建 TCP 链接,以及若是维持链接,那么 SSL 的开销也能够避免,两张图片是我短期内两次访问 https://www.github.com 的时间统计:chrome-devtools

 

头一次访问,有初始化链接和 SSL 开销

 

初始化链接和 SSL 开销消失了,说明使用的是同一个 TCP 链接

持久链接:既然维持 TCP 链接好处这么多,HTTP/1.1 就把 Connection 头写进标准,而且默认开启持久链接,除非请求中写明 Connection: close,那么浏览器和服务器之间是会维持一段时间的 TCP 链接,不会一个请求结束就断掉。

因此第一个问题的答案是:默认状况下创建 TCP 链接不会断开,只有在请求报头中声明 Connection: close 才会在请求完成后关闭链接。

 

第二个问题

一个 TCP 链接能够对应几个 HTTP 请求?

了解了第一个问题以后,其实这个问题已经有了答案,若是维持链接,一个 TCP 链接是能够发送多个 HTTP 请求的。

第三个问题

一个 TCP 链接中 HTTP 请求发送能够一块儿发送么(好比一块儿发三个请求,再三个响应一块儿接收)?

HTTP/1.1 存在一个问题,单个 TCP 链接在同一时刻只能处理一个请求,意思是说:两个请求的生命周期不能重叠,任意两个 HTTP 请求从开始到结束的时间在同一个 TCP 链接里不能重叠。

虽然 HTTP/1.1 规范中规定了 Pipelining 来试图解决这个问题,可是这个功能在浏览器中默认是关闭的。

先来看一下 Pipelining 是什么,RFC 2616 中规定了:

A client that supports persistent connections MAY "pipeline" its requests (i.e., send multiple requests without waiting for each response). A server MUST send its responses to those requests in the same order that the requests were received. 一个支持持久链接的客户端能够在一个链接中发送多个请求(不须要等待任意请求的响应)。收到请求的服务器必须按照请求收到的顺序发送响应。

至于标准为何这么设定,咱们能够大概推测一个缘由:因为 HTTP/1.1 是个文本协议,同时返回的内容也并不能区分对应于哪一个发送的请求,因此顺序必须维持一致。好比你向服务器发送了两个请求 GET/query?q=A 和 GET/query?q=B,服务器返回了两个结果,浏览器是没有办法根据响应结果来判断响应对应于哪个请求的。

Pipelining 这种设想看起来比较美好,可是在实践中会出现许多问题:

  • 一些代理服务器不能正确的处理 HTTP Pipelining。

  • 正确的流水线实现是复杂的。

  • Head-of-line Blocking 链接头阻塞:在创建起一个 TCP 链接以后,假设客户端在这个链接连续向服务器发送了几个请求。按照标准,服务器应该按照收到请求的顺序返回结果,假设服务器在处理首个请求时花费了大量时间,那么后面全部的请求都须要等着首个请求结束才能响应。

因此现代浏览器默认是不开启 HTTP Pipelining 的。

可是,HTTP2 提供了 Multiplexing 多路传输特性,能够在一个 TCP 链接中同时完成多个 HTTP 请求。至于 Multiplexing 具体怎么实现的就是另外一个问题了。咱们能够看一下使用 HTTP2 的效果。

 

绿色是发起请求到请求返回的等待时间,蓝色是响应的下载时间,能够看到都是在同一个 Connection,并行完成的

因此这个问题也有了答案:在 HTTP/1.1 存在 Pipelining 技术能够完成这个多个请求同时发送,可是因为浏览器默认关闭,因此能够认为这是不可行的。在 HTTP2 中因为 Multiplexing 特色的存在,多个 HTTP 请求能够在同一个 TCP 链接中并行进行。

那么在 HTTP/1.1 时代,浏览器是如何提升页面加载效率的呢?主要有下面两点:

  1. 维持和服务器已经创建的 TCP 链接,在同一链接上顺序处理多个请求。

  2. 和服务器创建多个 TCP 链接。

第四个问题

为何有的时候刷新页面不须要从新创建 SSL 链接?

在第一个问题的讨论中已经有答案了,TCP 链接有的时候会被浏览器和服务端维持一段时间。TCP 不须要从新创建,SSL 天然也会用以前的。

第五个问题

浏览器对同一 Host 创建 TCP 链接到数量有没有限制?

假设咱们还处在 HTTP/1.1 时代,那个时候没有多路传输,当浏览器拿到一个有几十张图片的网页该怎么办呢?确定不能只开一个 TCP 链接顺序下载,那样用户确定等的很难受,可是若是每一个图片都开一个 TCP 链接发 HTTP 请求,那电脑或者服务器均可能受不了,要是有 1000 张图片的话总不能开 1000 个TCP 链接吧,你的电脑赞成 NAT 也不必定会赞成。

因此答案是:有。Chrome 最多容许对同一个 Host 创建六个 TCP 链接。不一样的浏览器有一些区别。

https://developers.google.com/web/tools/chrome-devtools/network/issues#queued-or-stalled-requestsdevelopers.google.com

那么回到最开始的问题,收到的 HTML 若是包含几十个图片标签,这些图片是以什么方式、什么顺序、创建了多少链接、使用什么协议被下载下来的呢?

若是图片都是 HTTPS 链接而且在同一个域名下,那么浏览器在 SSL 握手以后会和服务器商量能不能用 HTTP2,若是能的话就使用 Multiplexing 功能在这个链接上进行多路传输。不过也未必会全部挂在这个域名的资源都会使用一个 TCP 链接去获取,可是能够肯定的是 Multiplexing 极可能会被用到。

若是发现用不了 HTTP2 呢?或者用不了 HTTPS(现实中的 HTTP2 都是在 HTTPS 上实现的,因此也就是只能使用 HTTP/1.1)。那浏览器就会在一个 HOST 上创建多个 TCP 链接,链接数量的最大限制取决于浏览器设置,这些链接会在空闲的时候被浏览器用来发送新的请求,若是全部的链接都正在发送请求呢?那其余的请求就只能等等了。

posted on 2020-08-01 13:06  围龙小子  阅读(313)  评论(0编辑  收藏  举报