Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第 140 题:为什么 HTTP1.1 不能实现多路复用 #290

Open
yygmind opened this issue Oct 14, 2019 · 21 comments
Open

第 140 题:为什么 HTTP1.1 不能实现多路复用 #290

yygmind opened this issue Oct 14, 2019 · 21 comments

Comments

@yygmind
Copy link
Contributor

yygmind commented Oct 14, 2019

No description provided.

@yygmind yygmind added the 腾讯 label Oct 14, 2019
@yygmind yygmind changed the title 第 140 题:为什么 Http1.1 不能实现多路复用 第 140 题:为什么 HTTP1.1 不能实现多路复用 Oct 14, 2019
@Loading-m
Copy link

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

@qitian7
Copy link

qitian7 commented Oct 14, 2019

HTTP1.x是序列和阻塞机制

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

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

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

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

@heart-er
Copy link

heart-er commented Oct 14, 2019

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

@Mrlie
Copy link

Mrlie commented Oct 14, 2019

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

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

@mayloveless
Copy link

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

@teachat8
Copy link

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

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

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

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

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

@teachat8
Copy link

分享一篇文章 https://aotu.io/notes/2016/06/14/http2/index.html

@mengsixing
Copy link

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

@webgzh907247189
Copy link

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

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

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

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

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

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

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

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

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

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

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

@Arsenalfanscz
Copy link

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

@irina9215
Copy link

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

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

@yygmind yygmind added the 网络 label Dec 16, 2019
@bbrucechen
Copy link

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

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

@blade254353074
Copy link

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

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

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

@PeterRock
Copy link

这篇写得不错 HTTP/1.0、HTTP/1.1、HTTP/2、HTTPS

@JaykeyGuo
Copy link

JaykeyGuo commented Oct 27, 2020

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

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

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

@yft
Copy link

yft commented Aug 11, 2021

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

image

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

@wd2010
Copy link

wd2010 commented Dec 22, 2021

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文件,被浏览器扔掉,至此,没有正确请求到内容。

@wd2010
Copy link

wd2010 commented Dec 22, 2021

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在传输单个流时有资源空隙,空隙后面的部分仍会被阻塞,直到这个间隙被填完整。

@oborc
Copy link

oborc commented Sep 18, 2023

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

为什么明文不能分帧?

@SayerHe
Copy link

SayerHe commented Oct 31, 2023

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

我理解不应该是 二进制 和 文本 的区别,而应该是HTTP2.0中引入了 流和帧 的概念,从而让服务端可以将请求与响应对应起来。而二进制的主要作用在于压缩报文

@keven-wang
Copy link

keven-wang commented Apr 2, 2024

其实看怎么定义多路复用了,如果多路复用指的的事:一个 TCP 连接传输多个 HTTP 返回,HTTP 1.1 是有这个能力的,但因为 HTTP1.1 在一个 TCP 管道内的多个返回是采用排队的形式传送的,传输调度的单位是请求返回,所以一个返回阻塞了后面就要排队。而 HTTP2.0 对可以对 HTTP 响应内容的 Header 和 Body 拆分为数据帧,所以不会出现一个文件传输阻塞后,后面需要排队的问题。因为大家都拆分为数据帧了,传输后完成后才组装。

所以 HTTP1.1 和 HTTP2.0 都对一个 TCP 连接做了复用的优化,只是对传输的粒度控制不一样,HTTP2.0 粒度更小,效果更高,但丢包后的影响会更大,所以才有了 HTTP3.0 对整个进行优化。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests