-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
Comments
可能是因为http1.1是纯文本吧,不是二进制帧的原因? |
HTTP1.x是序列和阻塞机制 HTTP 2.0 是多工复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"。
|
1.首先http 1.1是一个慢启动的过程,在起初建立连接的时候会慢慢的寻找一个稳定的速度,然后选择这个稳定速度的大小来进行发送数据 |
感觉跟1,2没关系,现在TCP RENO版本的拥塞控制设计都是先进入慢启动阶段,无论http1.1 还是http2.0 |
没有序列号标签,交错发送无法排序 |
HTTP/1.x 有个问题叫队头阻塞,即一个连接同时只能有效地承载一个请求。 HTTP/1.1 试过用流水线来解决这个问题,但是效果并不理想(数据量较大或者速度较慢的响应,仍然会阻碍排在后面的响应)。此外,由于网络中介和服务器都不能很好的支持流水线技术,导致部署起来困难重重。 客户端被迫使用一些启发式的算法(基本靠猜)来决定哪些连接来承载哪些请求;由于通常一个页面加载资源的连接需求,往往超过了可用连接资源的 10 倍,这对性能产生极大的负面影响,后果经常是引起了风暴式的阻塞。 而多路复用则能很好的解决这些问题,因为它能同时处理多个消息的请求和响应;甚至可以在传输过程中将一个消息跟另外一个糅合在一起。 所以客户端只需要一个连接就能加载一个完整的页面。 |
HTTP/1.1 不是二进制传输,而是通过文本进行传输。由于没有流的概念,在使用并行传输(多路复用)传递数据时,接收端在接收到响应后,并不能区分多个响应分别对应的请求,所以无法将多个响应的结果重新进行组装,也就实现不了多路复用。 |
http2.0 也存在队头阻塞问题,如果造成队头阻塞,问题可能比http1.1还严重,因为只有一个tcp连接,后续的传输都要等前面,http1.1 多个tcp连接,阻塞一个,其他的还可以正常跑 |
对的,这也是为什么http3.0出来的主要原因之一 |
多路复用归功于, HTTP/2 中的 帧(frame)和流(stream)。帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。就是在一个 TCP 连接中可以存在多条流。 而Http 1.x 并没有这个标识,每次请求都会建立一次HTTP连接,3次握手4次挥手。 |
终于有个回答到点子上的了 |
HTTP 1.1 默认有 Keep-Alive,建立连接的一定时间内,不会每次请求都建立新的 TCP 连接。 |
多路复用:实际上是指一个TCP/IP链接提供给多个HTTP链接使用,在1.1的标准中,HTTP还是以文本的方式传输,有响应和应答的方式,这样确保能到达另一端,但这也带了的问题就是需要等待响应。一个socket链接只能给一个HTTP使用,不能把一个完整的文本拆成流来处理,比较优化的方式也是只是根据Content-length来分割,最后让浏览器来拼装。 核心是HTTP1.1是基于文本的传输方式,还是一个一个文件。 而HTTP2.0则是使用流的方式来传输,可以把一个文件分包,根据Id来拼装,做到一个socket链接中传输多个文件的需求。 |
短连接、keep-alive、pipelining ,三种情况下的请求与tcp连接关系: |
HTTP/1.1是纯文本协议,将明文载荷和请求头传递给TCP。但对于TCP来说,上层传递的数据是不透明的字节流,无法区分是何种资源(不管上层传递的是js,css还是html,对于TCP来说都是一堆没有啥区别的字节流)。假设HTTP/1.1使用一个TCP连接来实现多路复用,现在要传输 // 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: function a() {console.log('a')}
HTTP/1.1 200 OK
Content-Length: 600
div { font-size: 18px; } 由于index.js不是有效的js文件,被浏览器扔掉,至此,没有正确请求到内容。 |
HTTP/3.0也有队头阻塞,http3使用QUIC传输,在QUIC使用帧标记资源,解决了TCP无法识别资源的问题,但QUIC在传输单个资源时是按顺序的(QUIC跨流的顺序不是有序的),意味着如果QUIC在传输单个流时有资源空隙,空隙后面的部分仍会被阻塞,直到这个间隙被填完整。 |
为什么明文不能分帧? |
我理解不应该是 二进制 和 文本 的区别,而应该是HTTP2.0中引入了 流和帧 的概念,从而让服务端可以将请求与响应对应起来。而二进制的主要作用在于压缩报文 |
其实看怎么定义多路复用了,如果多路复用指的的事:一个 TCP 连接传输多个 HTTP 返回,HTTP 1.1 是有这个能力的,但因为 HTTP1.1 在一个 TCP 管道内的多个返回是采用排队的形式传送的,传输调度的单位是请求返回,所以一个返回阻塞了后面就要排队。而 HTTP2.0 对可以对 HTTP 响应内容的 Header 和 Body 拆分为数据帧,所以不会出现一个文件传输阻塞后,后面需要排队的问题。因为大家都拆分为数据帧了,传输后完成后才组装。 所以 HTTP1.1 和 HTTP2.0 都对一个 TCP 连接做了复用的优化,只是对传输的粒度控制不一样,HTTP2.0 粒度更小,效果更高,但丢包后的影响会更大,所以才有了 HTTP3.0 对整个进行优化。 |
No description provided.
The text was updated successfully, but these errors were encountered: