我正在参与掘金创作者训练营第5期, 点击了解活动详情
初生:HTTP/0.9
HTTP(Hyper Text Transfer Protocol)协议,全称超文本传输协议。之所以称为超文本传输协议,是因为HTTP在设计之初(HTTP/0.9版本),仅需要传递HTML超文本内容。在那个Web1.0的时代,用户在页面上还无法进行交互,页面仅展示内容。此时,只要服务端能够响应客户端的请求就足够了,这就对应了GET
请求。
传统意义上的HTTP都需要依赖于TCP建立链接,客户端向服务端发送请求后,服务端将制定的HTML超文本内容以ASCII码的方式返回给客户端。数据传输完成之后,链接就断开了。
承载内容催生的进化:HTTP/1.0与HTTP/1.1
随着互联网的快速普及,特别是JavaScript与CSS的出现,让枯燥单调的纯HTML页面生动了起来,图片、音频、视频等非HTML超文本内容也开始在页面中出现。而原先的HTTP/0.9仅能保证HTML超文本内容的传输,这就推动了HTTP的第一次进化——HTTP需要承载更多类型的内容传输。
从单纯的HTML超文本内容传输,到多类型的内容传输是一次质的改变。这其中,最大的问题是如何支持多类型文件的传输?类型更多样了,就需要能够更准确的告诉服务端我们需要什么。HTTP/1.0的解决方案是,添加请求头和响应头。请求头相当于客户端告诉服务端,我需要的东西长什么样;而响应头相当于服务端告诉客户端,我给你的东西长这样。
/*请求头*/
accept: text/html // 文件类型
accept-encoding: gzip, deflate, br // 支持的压缩方式
accept-Charset: ISO-8859-1,utf-8 // 支持的编码格式
/*响应头*/
content-encoding: br // 给客户端的压缩方式
content-type: text/html; charset=UTF-8 // 文件类型 与 编码格式
从上例中可以看出,响应头给出的返回结果,只需要满足一组可被客户端解析的条件即可。除了文件类型外,客户端和服务端还需要协商编码格式与压缩方式(减小传递文件的体积)。
更多更大的传递内容意味着用户需要更长时间的等待,服务端需要处理更多的请求。HTTP当然也考虑到了这一点,在1.0版本中引入了缓存机制。已经从服务器上请求过的数据,会被缓存到客户端,无需再通过请求服务端来获取资源。
内容的多样性与机制的增加,导致了在HTTP请求和响应的过程中,可能会出现各种各样的情况。为了方便让使用者判断HTTP的状态,HTTP约定了状态码,状态码反应了HTTP的状态,并通过响应行的方式告知客户端。
随着技术的进一步发展,HTTP/1.0已经无法满足需要了。HTTP/1.1针对不同需求的出现,在HTTP/1.0的基础上做了针对性的改进。
- 提升传输速度:持久连接,TCP连接主动持续保持,为每个域名维护6个TCP管道;
- 支持虚拟主机:Host字段,使用Host字段来表示域名;
- 支持登录保持:Cookie机制,携带Cookie标识用户身份;
- 引入安全机制:对称加密,非对称加密;
网络速度催生的进化:HTTP/2.0
网络世界越来越丰富多彩,页面上承载的内容也越来越重,HTTP/1.1也渐渐有些不够用了。由此引发了三个问题。
网页中要下载的内容越来越多了,我们无法在瞬间完成所有内容的下载。在这么多的内容中总会分出个轻重缓急,既然无法瞬间下载,我先下载最重要的内容总可以吧。可惜在HTTP/1.1中不可以,HTTP/1.1不存在一种协商机制,能够使得高优先级的文件可以被先下载,这是第一个问题。
既然无法优先下载高优先级的数据,能不能想办法提高TCP管道的利用率从而提高下载速度呢?比如试试并行?可惜也不行,HTTP/1.1是基于简单的文本协议进行传递,必须挨个传递,想要在TCP管道中并行传输,就需要包含流ID的消息格式,使得并行乱序发送的消息能够根据流ID进行关联匹配。因此,HTTP/1.1也无法有效利用TCP管道进行并行传输,这是第二个问题。
正是由于HTTP/1.1无法在TCP管道中并行传输,我们不得不通过增加TCP管道的数量来提高数据的传输速度。过分依赖TCP就必然要收到TCP自身缺陷的影响。TCP为了避免网络拥塞,会采取一种慢启动的策略,从而导致大量的时间开销,这是第三个问题。
为了解决这些问题,HTTP/2.0来了。只要能实现TCP管道的并行传输,重复利用TCP管道保证传输速率,就无需再开多余的TCP管道,这样一来,我们就能直接解决问题二和问题三。为此,HTTP/2.0引入了多路复用机制。
多路复用的核心就是问题二中说到的解决方式,给每份数据打上ID,服务器即使并发乱序向客户端发送数据,浏览器在接收到数据后,仍然可以按照ID拼接这些HTTP响应数据。为了解决问题一,在实践中,多路复用还可将请求进行分帧传输,这种传输方式的好处在于,请求可以被暂停。请求被分帧切片,遇到高优先级的请求需要发送时,可以立即停止当前的请求,发送高优先级请求。
HTTP/2.0为了兼容HTTP/1.1,数据的分帧与合并专门由二进制分帧层处理,使得HTTP/2.0在使用上与HTTP/1.1的规则语法并未发生较大改变,改变的只有被封装的传输方式,这也使得HTTP/2.0可以快速被推广普及。
此外,为了提高页面首次的打开速度,HTTP/2.0还能够预解析HTML中的文件,如JavaScript文件、CSS文件,提前将其推送到客户端浏览器。压缩请求头与响应头的体积,也能在一定程度上提高传输效率。
祖宗之法可变,传输效率的究极进化:HTTP/3.0
受限于时代的技术背景,HTTP一直以来都是基于TCP进行工作的,这样的依赖关系就意味着,无论对上层的HTTP如何优化,都无法屏蔽掉下层TCP的缺点。HTTP/2.0已经把上层的HTTP尽可能的优化了,再想有较大的提升,我们只能考虑从TCP下手。
回忆一下课本上的计算机网络知识,TCP是可靠传输。为了保证传输是可靠的,TCP在传输中一旦发生丢包,就需要进行重传。而重传会导致其余的数据需要等待重传完毕,才能继续传输。这会对HTTP造成什么影响呢?在HTTP/2.0中,我们为了避免TCP慢启动带来的时延,仅使用一条TCP管道进行数据传输。TCP丢包时,会阻塞所有请求,这时,HTTP/2.0对TCP管道的高利用率就被抹去了,这是问题一。即使TCP在传输过程中不丢包,TCP建立连接时需要的三次握手过程,也会造成一定的时延,这是问题二。
是否能通过优化TCP来规避掉这些问题呢?优化TCP本身是没问题的,问题在于TCP协议过于底层,很难对其进行更新。在PC中,TCP协议是由操作系统内核所控制,更不要说,大量的中间网络设备中存在的TCP。所以我们只能另想办法,这就用到了QUIC(Quick UDP Internet Connection),由谷歌制定的一种基于UDP的低延时互联网传输层协议。
QUIC最大的改进在于,在同一物理连接上,可以同时存在多个独立的数据流,这解决了TCP的问题一。其次,基于UDP协议的QUIC,不需要三次握手,就可以快速建立连接,这解决了TCP的问题二。同时,QUIC也保留了TCP可靠传输,拥塞控制等优点。