第 140 题:为什么 HTTP 1.1 不能实现多路复用

发布于 2022-05-04 10:10:51 字数 694 浏览 1242 评论 18

在一文走进 TCP 与 HTTP 中,我们介绍过,HTTP 协议是建立在 TCP 协议上的应用层协议, HTTP 协议最初是一个非常简单的协议,通信方式也是采取简答的请求-应答的模式,即:客户端与服务器端的的每次请求都需要创建 TCP 连接,服务器响应后断开 TCP 连接,再请求再创建断开。

在 HTTP/0.9 与 早期 HTTP/1.0 中,默认的就是这种,但这种频繁的创建、断开连接无疑是极大的消耗性能

TCP连接的新建成本很高,因为客户端和服务器建立连接时需要“三次握手”,发送 3 个数据包,需要 1 个 RTT;关闭连接是“四次挥手”,4 个数据包需要 2 个 RTT,并且开始时发送速率较慢(slow start),随着网页加载的外部资源越来越多,这个问题就愈发突出了

所以 HTTP/1.0 引入了 keep-alive 长连接,HTTP/1.0 中是默认关闭的,可以通过 Connection: keep-alive; 开启 ,HTTP/1.1 默认是开启的,无论加没加 Connection: keep-alive;

所谓长连接,即在 HTTP 请求建立 TCP 连接时,请求结束,TCP 连接不断开,继续保持一段时间(timeout),在这段时间内,同一客户端向服务器发送请求都会复用该 TCP 连接,并重置 timeout 时间计数器,在接下来 timeout 时间内还可以继续复用 TCP 。这样无疑省略了反复创建和销毁 TCP 连接的损耗。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

⊕婉儿 2022-05-04 13:54:36

HTTP/1.x 有个问题叫队头阻塞,即一个连接同时只能有效地承载一个请求。

HTTP/1.1 试过用流水线来解决这个问题,但是效果并不理想(数据量较大或者速度较慢的响应,仍然会阻碍排在后面的响应)。此外,由于网络中介和服务器都不能很好的支持流水线技术,导致部署起来困难重重。

客户端被迫使用一些启发式的算法(基本靠猜)来决定哪些连接来承载哪些请求;由于通常一个页面加载资源的连接需求,往往超过了可用连接资源的 10 倍,这对性能产生极大的负面影响,后果经常是引起了风暴式的阻塞。

而多路复用则能很好的解决这些问题,因为它能同时处理多个消息的请求和响应;甚至可以在传输过程中将一个消息跟另外一个糅合在一起。

所以客户端只需要一个连接就能加载一个完整的页面。

HTTP/1.x 有个问题叫队头阻塞,即一个连接同时只能有效地承载一个请求。

HTTP/1.1 试过用流水线来解决这个问题,但是效果并不理想(数据量较大或者速度较慢的响应,仍然会阻碍排在后面的响应)。此外,由于网络中介和服务器都不能很好的支持流水线技术,导致部署起来困难重重。

客户端被迫使用一些启发式的算法(基本靠猜)来决定哪些连接来承载哪些请求;由于通常一个页面加载资源的连接需求,往往超过了可用连接资源的 10 倍,这对性能产生极大的负面影响,后果经常是引起了风暴式的阻塞。

而多路复用则能很好的解决这些问题,因为它能同时处理多个消息的请求和响应;甚至可以在传输过程中将一个消息跟另外一个糅合在一起。

所以客户端只需要一个连接就能加载一个完整的页面。

http2.0 也存在队头阻塞问题,如果造成队头阻塞,问题可能比http1.1还严重,因为只有一个tcp连接,后续的传输都要等前面,http1.1 多个tcp连接,阻塞一个,其他的还可以正常跑

浅唱ヾ落雨殇 2022-05-04 13:54:36

HTTP/1.x 有个问题叫队头阻塞,即一个连接同时只能有效地承载一个请求。
HTTP/1.1 试过用流水线来解决这个问题,但是效果并不理想(数据量较大或者速度较慢的响应,仍然会阻碍排在后面的响应)。此外,由于网络中介和服务器都不能很好的支持流水线技术,导致部署起来困难重重。
客户端被迫使用一些启发式的算法(基本靠猜)来决定哪些连接来承载哪些请求;由于通常一个页面加载资源的连接需求,往往超过了可用连接资源的 10 倍,这对性能产生极大的负面影响,后果经常是引起了风暴式的阻塞。
而多路复用则能很好的解决这些问题,因为它能同时处理多个消息的请求和响应;甚至可以在传输过程中将一个消息跟另外一个糅合在一起。
所以客户端只需要一个连接就能加载一个完整的页面。

HTTP/1.x 有个问题叫队头阻塞,即一个连接同时只能有效地承载一个请求。
HTTP/1.1 试过用流水线来解决这个问题,但是效果并不理想(数据量较大或者速度较慢的响应,仍然会阻碍排在后面的响应)。此外,由于网络中介和服务器都不能很好的支持流水线技术,导致部署起来困难重重。
客户端被迫使用一些启发式的算法(基本靠猜)来决定哪些连接来承载哪些请求;由于通常一个页面加载资源的连接需求,往往超过了可用连接资源的 10 倍,这对性能产生极大的负面影响,后果经常是引起了风暴式的阻塞。
而多路复用则能很好的解决这些问题,因为它能同时处理多个消息的请求和响应;甚至可以在传输过程中将一个消息跟另外一个糅合在一起。
所以客户端只需要一个连接就能加载一个完整的页面。

http2.0 也存在队头阻塞问题,如果造成队头阻塞,问题可能比http1.1还严重,因为只有一个tcp连接,后续的传输都要等前面,http1.1 多个tcp连接,阻塞一个,其他的还可以正常跑

对的,这也是为什么http3.0出来的主要原因之一
在HTTP/2中,多个请求是跑在一个TCP管道中的。但当出现了丢包时,HTTP/2 的表现反倒不如 HTTP/1 了。因为TCP为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,HTTP/2出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该TCP连接中的所有请求(如下图)。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。
https://mp.weixin.qq.com/s/sakIv-NidqkO1tviBHxtWQ

柠檬色的秋千 2022-05-04 13:54:36

多路复用归功于, HTTP/2 中的 帧(frame)和流(stream)。帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。就是在一个 TCP 连接中可以存在多条流。

而Http 1.x 并没有这个标识,每次请求都会建立一次HTTP连接,3次握手4次挥手。

别念他 2022-05-04 13:54:36

HTTP/1.1 不是二进制传输,而是通过文本进行传输。由于没有流的概念,在使用并行传输(多路复用)传递数据时,接收端在接收到响应后,并不能区分多个响应分别对应的请求,所以无法将多个响应的结果重新进行组装,也就实现不了多路复用。

终于有个回答到点子上的了

相守太难 2022-05-04 13:54:36

多路复用归功于, HTTP/2 中的 帧(frame)和流(stream)。帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。就是在一个 TCP 连接中可以存在多条流。

而Http 1.x 并没有这个标识,每次请求都会建立一次HTTP连接,3次握手4次挥手。

HTTP 1.1 默认有 Keep-Alive,建立连接的一定时间内,不会每次请求都建立新的 TCP 连接。

与君绝 2022-05-04 13:54:36

多路复用:实际上是指一个TCP/IP链接提供给多个HTTP链接使用,在1.1的标准中,HTTP还是以文本的方式传输,有响应和应答的方式,这样确保能到达另一端,但这也带了的问题就是需要等待响应。一个socket链接只能给一个HTTP使用,不能把一个完整的文本拆成流来处理,比较优化的方式也是只是根据Content-length来分割,最后让浏览器来拼装。

核心是HTTP1.1是基于文本的传输方式,还是一个一个文件。

而HTTP2.0则是使用流的方式来传输,可以把一个文件分包,根据Id来拼装,做到一个socket链接中传输多个文件的需求。

洋洋洒洒 2022-05-04 13:54:36

短连接、keep-alive、pipelining ,三种情况下的请求与tcp连接关系:

—— HTTP/1.x 的连接管理

小情绪 2022-05-04 13:54:36

HTTP/1.1是纯文本协议,将明文载荷和请求头传递给TCP。但对于TCP来说,上层传递的数据是不透明的字节流,无法区分是何种资源(不管上层传递的是js,css还是html,对于TCP来说都是一堆没有啥区别的字节流)。假设HTTP/1.1使用一个TCP连接来实现多路复用,现在要传输index.jsstyle.css两个文件,内容如下:

// index.js
function a() {console.log('a')}
function b() {console.log('b')}
// style.css
div { font-size: 18px; }
span { color: red; }
# index.js的请求头
HTTP/1.1 200 OK
Content-Length: 1000
# style.css的请求头
HTTP/1.1 200 OK
Content-Length: 600

注: 两个文件数据载荷+请求头混淆地穿插在一个TCP中(单个文件的数据载荷+请求头是有序的)。

浏览器在开始分析js时,期望有1000个字节(content-length),当接收到600个字节时,开始读取css文件,最终将css头部和一部分css内容当做js,TCP此时已经接收了2个包,第3个包传输剩余的css,由于第3个包已经没有了头部信息,浏览器直接扔掉。由于前面接收到的js不是有效的js内容,最终失败。

传输的3个包packet1, packet2, packet3,内容分别如下:

// packet1
HTTP/1.1 200 OK
Content-Length: 1000
function a() {console.log('a')}
// packet2
div { font-size: 18px; }
HTTP/1.1 200 OK
Content-Length: 600
// packet3 没有头部信息,被浏览器扔掉
function b() {console.log('b')}
span { color: red; }

最终packet1和packet2合成index.js交有浏览器处理
头信息为(index.js的头信息当做头信息,style.css的头信息当做index.js内容的一部分):

HTTP/1.1 200 OK
Content-Length: 1000

index.js:

function a() {console.log('a')}
HTTP/1.1 200 OK
Content-Length: 600
div { font-size: 18px; }

由于index.js不是有效的js文件,被浏览器扔掉,至此,没有正确请求到内容。

無處可尋 2022-05-04 13:54:36

HTTP/1.x 有个问题叫队头阻塞,即一个连接同时只能有效地承载一个请求。
HTTP/1.1 试过用流水线来解决这个问题,但是效果并不理想(数据量较大或者速度较慢的响应,仍然会阻碍排在后面的响应)。此外,由于网络中介和服务器都不能很好的支持流水线技术,导致部署起来困难重重。
客户端被迫使用一些启发式的算法(基本靠猜)来决定哪些连接来承载哪些请求;由于通常一个页面加载资源的连接需求,往往超过了可用连接资源的 10 倍,这对性能产生极大的负面影响,后果经常是引起了风暴式的阻塞。
而多路复用则能很好的解决这些问题,因为它能同时处理多个消息的请求和响应;甚至可以在传输过程中将一个消息跟另外一个糅合在一起。
所以客户端只需要一个连接就能加载一个完整的页面。

HTTP/1.x 有个问题叫队头阻塞,即一个连接同时只能有效地承载一个请求。
HTTP/1.1 试过用流水线来解决这个问题,但是效果并不理想(数据量较大或者速度较慢的响应,仍然会阻碍排在后面的响应)。此外,由于网络中介和服务器都不能很好的支持流水线技术,导致部署起来困难重重。
客户端被迫使用一些启发式的算法(基本靠猜)来决定哪些连接来承载哪些请求;由于通常一个页面加载资源的连接需求,往往超过了可用连接资源的 10 倍,这对性能产生极大的负面影响,后果经常是引起了风暴式的阻塞。
而多路复用则能很好的解决这些问题,因为它能同时处理多个消息的请求和响应;甚至可以在传输过程中将一个消息跟另外一个糅合在一起。
所以客户端只需要一个连接就能加载一个完整的页面。

http2.0 也存在队头阻塞问题,如果造成队头阻塞,问题可能比http1.1还严重,因为只有一个tcp连接,后续的传输都要等前面,http1.1 多个tcp连接,阻塞一个,其他的还可以正常跑

对的,这也是为什么http3.0出来的主要原因之一 在HTTP/2中,多个请求是跑在一个TCP管道中的。但当出现了丢包时,HTTP/2 的表现反倒不如 HTTP/1 了。因为TCP为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,HTTP/2出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该TCP连接中的所有请求(如下图)。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。 https://mp.weixin.qq.com/s/sakIv-NidqkO1tviBHxtWQ

HTTP/3.0也有队头阻塞,http3使用QUIC传输,在QUIC使用帧标记资源,解决了TCP无法识别资源的问题,但QUIC在传输单个资源时是按顺序的(QUIC跨流的顺序不是有序的),意味着如果QUIC在传输单个流时有资源空隙,空隙后面的部分仍会被阻塞,直到这个间隙被填完整。

记忆消瘦 2022-05-04 13:54:34

HTTP/1.1 不是二进制传输,而是通过文本进行传输。由于没有流的概念,在使用并行传输(多路复用)传递数据时,接收端在接收到响应后,并不能区分多个响应分别对应的请求,所以无法将多个响应的结果重新进行组装,也就实现不了多路复用。

北城挽邺 2022-05-04 13:53:46

没有序列号标签,交错发送无法排序

別甾虛僞 2022-05-04 13:53:46

HTTP/1.x 有个问题叫队头阻塞,即一个连接同时只能有效地承载一个请求。

HTTP/1.1 试过用流水线来解决这个问题,但是效果并不理想(数据量较大或者速度较慢的响应,仍然会阻碍排在后面的响应)。此外,由于网络中介和服务器都不能很好的支持流水线技术,导致部署起来困难重重。

客户端被迫使用一些启发式的算法(基本靠猜)来决定哪些连接来承载哪些请求;由于通常一个页面加载资源的连接需求,往往超过了可用连接资源的 10 倍,这对性能产生极大的负面影响,后果经常是引起了风暴式的阻塞。

而多路复用则能很好的解决这些问题,因为它能同时处理多个消息的请求和响应;甚至可以在传输过程中将一个消息跟另外一个糅合在一起。

所以客户端只需要一个连接就能加载一个完整的页面。

天邊彩虹〆 2022-05-04 13:53:11

1.首先http 1.1是一个慢启动的过程,在起初建立连接的时候会慢慢的寻找一个稳定的速度,然后选择这个稳定速度的大小来进行发送数据
2.正是因为慢启动的原因,每个资源在传送的时候都会寻找一个合适的发送大小,因此会造成每个资源间的竞争,因此,在同时开启了多个tcp连接的情况下,这些资源都会去竞争带宽
3.http 1.1有一个队头阻塞的问题,当一个请求因为某些原因而在队头阻塞的时候,从而导致后面的请求被延迟,效率非常低

感觉跟1,2没关系,现在TCP RENO版本的拥塞控制设计都是先进入慢启动阶段,无论http1.1 还是http2.0

椒妓 2022-05-04 13:50:05

1.首先http 1.1是一个慢启动的过程,在起初建立连接的时候会慢慢的寻找一个稳定的速度,然后选择这个稳定速度的大小来进行发送数据
2.正是因为慢启动的原因,每个资源在传送的时候都会寻找一个合适的发送大小,因此会造成每个资源间的竞争,因此,在同时开启了多个tcp连接的情况下,这些资源都会去竞争带宽
3.http 1.1有一个队头阻塞的问题,当一个请求因为某些原因而在队头阻塞的时候,从而导致后面的请求被延迟,效率非常低

ま柒月。 2022-05-04 12:52:36

HTTP1.x是序列和阻塞机制

HTTP 2.0 是多工复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"。

  • 举例来说,在一个TCP连接里面,服务器同时收到了A请求和B请求,于是先回应A请求,结果发现处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。

  • 旧的http1.1是会等 A请求完全处理完后在 处理B请求,会阻塞

  • 另:http1.1已经实现了管道机制:即 在同一个TCP连接里面,客户端可以同时发送多个请求。http 1.0并做不到,所以效率很低

零度℉ 2022-05-04 12:44:18

可能是因为http1.1是纯文本吧,不是二进制帧的原因?

~没有更多了~

关于作者

0 文章
0 评论
964 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文