白话http队头阻塞

发布时间 2023-05-15 09:38:13作者: 沧海一滴

 

题图 From Bing By clm

http协议的1.0版本与1.1版本最大的一个区别就是http1.1增加了长连接功能,那什么是http的长连接呢?

在了解http的长连接之前,我们来看一下http1.0的请求是如何建立连接的,首先我们要清楚的是,http不论哪个版本,都是建立在tcp协议上的,而tcp的连接需要经历三次握手,tcp的关闭需要四次挥手,而http1.0协议中每个http请求都需要经历三次握手和四次挥手,页面中都多少个http请求,就需要建立多少次握手和挥手,流程如图:

但是在http1.1中新增了长连接功能,这个长连接功能通常被叫做http长连接,笔者认为这个叫法不太准确,应该叫做tcp长连接,为什么这样说呢,因为在http1.1中,如果页面中发起了多个http请求,此时只需要建立一个tcp连接就可以了,多个http请求响应会共用这一个tcp连接通道。

此时http请求中会携带一个Http请求头:Connection:keep-alive,现在大部分的web服务器都默认支持tcp长连接,也就是网页中的请求不携带Connection:keep-alive请求头,默认就是长连接请求,如果不想支持长连接的话,需要显示的添加Connection:closed请求头。

http1.1请求链接过程,如下:

通过对比两张流程图,我们发现,tcp保持长连接大大提高了传输效率,但是这里还是有一个问题,那就是http的对头阻塞问题。

在一般情况下,HTTP遵守“请求-响应”的模式,也就是客户端每次发送一个请求到服务端,服务端返回响应,这种模式很简单,但是有一个致命缺陷那就是页面中有多个请求,每个请求必须等到前一个请求响应之后才能发送,并且当前请求的响应返回之后,当前请求的下一个请求才能发送,流程如下图:

仔细观察上图:在tcp链接中,http请求必须等待前一个请求响应之后,才能发送,后面的依次类推,由此可以看出,如果在一个tcp通道中如果某个http请求的响应因为某个原因没有及时返回,后面的响应会被阻塞,这就是队头阻塞。

为了提高速度和效率,在持久连接的基础上,HTTP1.1进一步地支持在持久连接上使用管道化(pipelining)特性。管道化允许客户端在已发送的请求收到服务端的响应之前发送下一个请求,借此来减少等待时间提高吞吐,如果多个请求能在同一个TCP分节发送的话,还能提高网络利用率,流程如图:

仔细观察上图,我们发现,同一个tcp连接中可以同时发送多个http请求,也就是并发,但是在响应的时候,必须排队响应,谁先到达的谁先响应,相比不支持管道化的http请求确实提高了效率,但是还是有局限性,加入其中某个响应因为某种原因延迟了几秒,后面的响应都会被阻塞,如图:

观察上图红线标识的响应,因为红线标识的响应被阻塞了,它后面的所有响应都会被阻塞,这就是队头阻塞

并且使用HTTP管道化还有一些限制:

1、管道化要求服务端按照请求发送的顺序返回响应(FIFO),原因很简单,HTTP请求和响应并没有序号标识,无法将乱序的响应与请求关联起来。

2、当客户端在支持管道化时需要保持未收到响应的请求,当连接意外中断时,需要重新发送这部分请求。如果这个请求只是从服务器获取数据,那么并不会对资源造成任何影响,而如果是一个提交信息的请求如post请求,那么可能会造成资源多次提交从而改变资源,这是不允许的。而不会对服务器资源产生影响的请求有个专业名词叫做幂等请求。客户端在使用管道化的时候请求方式必须是幂等请求。

我将http不支持管道化与管道化的图放在一起,大家比较一下:

因为HTTP管道化本身可能会导致队头阻塞的问题,以及上面提到的一些限制,现代浏览器默认都关闭了管道化,并且大部分服务器也是默认不支持管道化的

那么如何解决队头阻塞呢?

HTTP 协议建议客户端使用并发长连接,注意这个并发指的是tcp并发连接接。RFC2616 里明确限制每个客户端可以建立两个长连接,这里着重说明一下,客户端建立长连接的个数是针对域名发起的,举例说明,当我们访问a.com网站的时候,客户端与a.com服务器建立的长链接就是2个。

但是一般浏览器会把并发链接数增加到6到8个,谷歌浏览器是6个,也就是页面中如果针对同一个域名有多个http请求,谷歌浏览器会针对这个域名建立6个tcp长连接,在每个长连接里面再去处理http请求,但是这种方案其实对服务器的挑战非常大,有些web优化方案中还会突破6到8的限制,那就是域名切片,因为长连接是针对的同一个域名,那么如果开发人员将资源分布在不同的域名上,那么长连接的数量也是可以被突破的。

假设页面中有100张图片,基于这个案例,咱们用图示将http1.0到http1.1的变迁用三张图来表示一下:

http1.0时代:100个http请求建立100个tcp连接。

http1.1时代,tcp支持了长连接,每个tcp可以处理多个http请求。

前面说过了,tcp的并发数可以通过域名切片来增大,但这样做会增大服务器的连接数,当服务器面对海量请求的话,可能会现问题,那么怎么办呢,这是就需要使用http2协议了。我们下期再聊。

下面我给大家总结一下本篇文章的内容:

1、首先我们厘清了一个概念,那就是http长连接其实指的是tcp长连接。

2、队头阻塞是一种现象,http因为请求-响应模型会有队头阻塞的现象出现,队头阻塞指的是在同一个tcp链接中,如果先发送的http请求如果没有响应的话,后面的http请求也不会响应。

3、解决队头阻塞的第一个方案就是并发长连接,浏览器默认是6-8个长连接,我们可以用域名分片的技术突破这个数值。

4、并发长连接虽然在一定程度上解决了http的队头阻塞,但是会对服务器的性能有较高的要求。

https://cloud.tencent.com/developer/beta/article/1509279