一、什么是队头阻塞
队头阻塞,在网络模型中简单理解就是,对于队列型的请求模型,如HTTP的请求-响应模型、TCP的ACK确认机制,都依赖得到一个具体的响应包,如果收不到这个响应包,那下一个请求就不能发,从而导致网络连接的阻塞。
二、HTTP队头阻塞
上面说了HTTP的队头阻塞原因是1.1的请求-响应模型,同一个连接中,下一个请求需要等上一个响应回来才能发出去;
那就很容易想到两种方式,要么同时建立多个连接,同时多个连接发送请求;但这样会有一些问题:
- 导致资源浪费,包括套接字、内存、网络带宽等;
- 实现复杂,客户端和服务器都要维护多个连接的状态,防止断开。
所以浏览器一般都会限制最大连接数;因此,这个方法能一定程度上解决队头阻塞问题,但效果一般。
那既然多个连接行不通,那就想办法让多个请求在一个连接上进行,即多组请求-响应复用同一个连接,多路复用技术。
那怎么实现呢?在HTTP2中,是通过把请求格式改造成二进制帧的方式。
客户端和服务器通过约定streamID的方式,来把同一个连接中,多个请求的stream帧区分组合,得到有效的请求数据。
通过这种方式,不同请求之间就不存在依赖关系了,HTTP2的队头阻塞问题也就解决。
三、TCP队头阻塞
因为HTTP是基于TCP的,所以对于上面的Stream帧,无论是改变编码方式、包结构等等方法,对于TCP连接来说,它接收到的都是字节流(没错,就是TCP的特性之一,基于字节流传输)。
TCP在实际发送连接的过程中,把数据分段传输,因此,一个HTTP请求也会被分段发送。而且TCP又是通过序列号来保证可靠传输的,必须要前面的数据都收到,服务端才能从缓冲区中完整读取数据。
因此如果TCP连接中有数据段丢失,那这个请求也就被阻塞了,即TCP的队头阻塞。
为了解决TCP队头阻塞,编写HTTP3的人想到了UDP,UDP没有可靠传输,在UDP的基础上,通过算法保证传输的可靠性,即QUIC,让HTTP3不再被TCP的队头阻塞困扰。
四、一些总结
在工作的一年中,接触了很多网络相关的问题与实现:
比如,通信协议:HTTP、TCP、QUIC、RPC、WebSocket,还有Socket4代理等等。
也遇到了一些实际的网络问题,像
- 海外,尤其是东南亚等网络基建不好的地区,为了解决网络延迟导致的用户体验差而引入QUIC;
- 为了降低网络连接建立的开销,引入长连接;
本文就尝试通过分析队头阻塞,也算是做一个简单的回顾~