Advanced-Frontend / Daily-Interview-Question

我是依扬(木易杨),公众号「高级前端进阶」作者,每天搞定一道前端大厂面试题,祝大家天天进步,一年后会看到不一样的自己。
https://muyiy.cn/question/
27.34k stars 3.29k forks source link

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

Open yygmind opened 4 years ago

Loading-m commented 4 years ago

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

qitian7 commented 4 years ago

HTTP1.x是序列和阻塞机制

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

heart-er commented 4 years ago

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

Mrlie commented 4 years ago

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

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

mayloveless commented 4 years ago

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

teachat8 commented 4 years ago

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

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

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

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

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

teachat8 commented 4 years ago

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

mengsixing commented 4 years ago

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

webgzh907247189 commented 4 years ago

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

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

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

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

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

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

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

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

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

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

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

Arsenalfanscz commented 4 years ago

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 commented 4 years ago

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

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

bbrucechen commented 4 years ago

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

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

blade254353074 commented 4 years ago

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

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

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

PeterRock commented 4 years ago

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

JaykeyGuo commented 3 years ago

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

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

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

yft commented 2 years ago

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

image

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

wd2010 commented 2 years ago

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 commented 2 years ago

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 commented 9 months ago

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

为什么明文不能分帧?

SayerHe commented 8 months ago

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

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

keven-wang commented 3 months ago

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

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