此学习记录来自于极客时间专栏浏览器工作原理与实践,由于个人对这块内容比较感兴趣,所以花钱买了专栏,但看完总觉得什么都没记住,所以将一些重要内容记录下来。文中有老师课程中的原文和配图,已在课程留言区告知老师,如侵删。
如果对这块内容感兴趣,强烈建议去读一遍老师的课程,很有收获。
浏览器中的网络
HTTP是浏览器中最重要且使用最多的协议,是浏览器和服务器之间的通信语言,也是互联网的基石。
超文本传输协议HTTP/0.9
由于需求简单,只进行传输体积很小的HTML文件,所以HTTP/0.9的实现有以下三个特点。
- 第一个是只有一个请求行,并没有HTTP请求头和请求体,因为只需要一个请求行就可以完整表达客户端的需求了。
- 第二个是服务器也没有返回头信息,这是因为服务器并不需要告诉客户端太多信息,只需要返回数据就可以了。
- 第三个是返回的文件内容是以ASCII字符流来传输的,因为都是HTML格式的文件,所以用ASCII字节码来传输是最合适的。
被浏览器推动的HTTP/1.0
随着时代的发展,浏览器中展示的不单单是HTML文件了,还包括了JavaScript、css、图片、音频、视频等不同类型的文件,因此支持多种类型的文件下载是HTTP/1.0的一个核心诉求,而且文件格式不仅仅局限于ASCII编码,还有很多其他类型编码的文件。
为了让客户端和服务端能够更深入地交流,HTTP/1.0引入了请求头和响应头,它们都是以Key-Value形式保存的,在HTTP发送请求时,会带上请求头信息,服务器返回数据时,会先返回响应头信息。
HTTP/1.0的请求响应流程accept: text/html
accept-encoding: gzip, deflate, br
accept-Charset: ISO-8859-1,utf-8
accept-language: zh-CN,zh
在发送请求时候会通过HTTP请求头告诉服务器它期待服务器返回什么类型的文件、采取什么形式的压缩、提供什么语言的文件以及文件的具体编码。最终发送出来的请求头内容如上所示。
其中
第一行代表期望服务器返回html类型的文件
第二行代表期望服务器可以采用gzip,default或者br其中的一种压缩方式
第三行代表期望返回的文件编码是UTF-8或者ISO-8859-1
第四行代表期望页面的优先语言是中文
服务器接收到浏览器发送过来的请求头信息之后,会根据请求头信息来准备响应数据,不过有时候会出现意外情况,比如浏览器请求的压缩类型是gzip,但是服务器不支持gzip,只支持br压缩,那么他会通过响应头中的content-encoding字段来告诉浏览器最终的压缩类型,也就是说最终浏览器需要根据响应头的信息来处理数据,下面是一段响应头中的数据信息。
content-encoding: br
content-type: text/html; charset=UTF-8
其中第一行表示服务器采用了br的压缩方法,第二行表示服务器返回的是html文件,并且编码是UTF-8.
有了响应头的信息,浏览器就会使用br方法来解压文件,再按照UTF-8的编码格式来处理原始文件,最后按照HTML的方式来解析该文件,这就是HTML/1.0的一个基本的处理流程。
HTTP/1.0除了对文件提供良好支持外,还依据当时的实际需求引入很多其他的特性,这些特性都是通过请求头和响应头来实现的。新增的其他特性都有:
- 有的请求服务器可能无法处理,或者处理出错,这时候就需要告诉浏览器服务器最终处理该请求的情况,这就引入了状态码。状态码是通过响应行的方式通知浏览器的。
- 为了减轻服务器的压力,在HTTP/1.0中提供了Cache机制,用来缓存已经下载过得数据。
- 服务器需要统计客户端的基础信息,比如Window和macOS的用户数量分别是多少,所以在HTTP/1.0的请求头中还加入了用户代理的字段。
缝缝补补的HTTP/1.1
随着技术的发展,HTTP/1.0也不能满足需求了,所以HTTP1.1又在HTTP1.0的基础上做了大量的更新。
- 改进持久连接。
HTTP1.0每进行一次HTTP通信,都需要建立TCP连接,传输HTTP数据和断开TCP连接三个阶段。当时由于通信文件较小,而且每个页面的引用不多,所以这种传输形式没什么问题,但是随着浏览器普及,单个页面中的图片文件越来越多,有时候一个页面可能包含了几百个外部引用的资源文件,如果在下载每个文件的时候,都要经历建立TCP连接,传输数据,和断开连接的步骤无疑会增加大量无谓开销。
为了解决这个问题,HTTP1.1中增加了持久连接的方法,他的特点是在一个TCP连接上可以传输多个HTTP请求,只要浏览器或者服务器没有明确断开连接,那么该TCP连接会一直保持
HTTP的持久连接可以有效减少TCP建立连接和断开连接的次数,这样的好处是减少服务器额外负担。
持久连接在HTTP/1.1中是默认开启的,所以不需要专门为了持久连接去HTTP请求头设置信息,如果不想采用持久连接,可以在HTTP请求头中加上Connection:close.目前浏览器中对于同一个域名,默认允许同时建立6个TCP持久连接。
- 客户端Cookie、安全机制
HTTP/1.1还引入了客户端Cookie机制和安全机制。
目前大多数网站都采用HTTP/1.1.
HTTP/2如何提升网络速度?
HTTP/1.1为网络效率做了大量优化,最核心如下
- 增加了持久连接
- 浏览器为每个域名最多同时维护6个TCP持久连接
- 使用CDN实现域名分片机制。
HTTP/1.1的主要问题
HTTP/1.1对宽带的利用率不理想。
宽带是指每秒最大能发送或者接收的字节数。我们把每秒能发送的最大字节数称为上行宽带,每秒能够接收的最大字节数称为下行宽带。
之所以说HTTP/1.1对宽带的利用率不是很理想,是因为HTTP/1.1很难将宽带用满。比如我们常说的100M带宽,实际的下载速度能达到12.5M/S,而采用HTTP/1.1时,也许在加载页面资源时最大只能使用到2.5M/S,很难将12.5M全部用满。
之所以会出现这个问题,主要是由以下三个原因导致。
第一个原因:TCP的慢启动
一旦一个TCP连接建立之后,就进入了发送数据状态,刚开始TCP协议会采用一个非常慢的速度去发送数据,然后慢慢加速,直到发送数据的速度达到一个理想状态,这个过程称为慢启动。
慢启动是TCP为了减少网络拥塞的一种策略,无法改变。
慢启动耗时比正常时间多很多,这样就推迟了宝贵的首次渲染页面的时间。
第二个原因,同时开启多条TCP连接,这些连接会竞争固定的宽带
当宽带充足时,每条连接发送或者接收速度会慢慢增加,而宽带不足时,TCP连接会减慢发送或者接收的速度,这样就会出现问题,有的TCP连接下载的是一些关键资源,如CSS文件,JavaScript文件,而有的TCP连接下载的是图片,视频,等普通的资源文件,但是多条TCP连接之间又不能协商让哪些关键资源优先下载,这样影响关键资源的下载速度。
第三个原因,HTTP/1.1对头阻塞的问题
在HTTP/1.1中使用持久连接,虽然能公用一个TCP管道,但是在一个管道中同一时刻只能处理一个请求,在当前请求没有结束之前,其他请求只能处于阻塞状态,这意味着我们不能随意在一个管道中发送请求和接收内容。
这是一个很严重的问题,因为阻塞请求的因素很多,并且都是不确定因素,假如有的请求被阻塞5s,那么后续的请求都要延迟5s,在这个等待过程中,带宽,CPU都被白白浪费了。
在浏览器生成页面的过程中,是非常希望能够提前接收到数据的,这样就可以对这些数据做预处理操作,比如提前接收到了图片,那么就可以提前进行编解码操作,等到需要使用该图片的时候,就可以直接给出处理后的数据了,这样能让用户感受到整体速度的提升。
但队头阻塞使得这些数据不能并行请求,所以队头阻塞是很不利于浏览器优化的。
HTTP/2的多路复用
HTTP/1.1所存在的一些主要问题,慢启动和TCP连接之间相互竞争带宽是由于TCP本身的机制导致的,而队头阻塞是由于HTTP/1.1的机制导致的。
HTTP/2的优化思路是一个域名只使用一个TCP长连接来传输数据,这样整个页面资源的下载过程只需要一次慢启动,同时也避免了多个TCP连接竞争带宽所带来的问题。
队头阻塞问题是等待请求完成后才能去请求下一个资源,这种方式无疑是最慢的,所以HTTP/2需要实现资源的并行请求,也就是任何时候都可以将请求发送给服务器,而并不需要等待其他请求的完成。然后服务器也可以随时返回处理好的请求资源给浏览器。
HTTP2的解决方案可以总结为:一个域名只使用一个TCP长连接和消除队头阻塞问题。
HTTP/2的多路复用该图就是HTTP/2最核心、最重要且最具颠覆性的多路复用机制。每个请求都有一个对应的ID,如stream1表示index.html请求,stream2表示foo.css的请求,这样在浏览器端,就可以随时将请求发送给服务器了。
服务器端接收到这些请求后,会根据自己的喜好来决定优先返回哪些内容,比如服务器可能早就缓存好了index.html和bar.js的响应头信息,那么当接收到请求的时候就可以立即把index.html和bar.js的响应头信息返回给浏览器,然后再将index.html和bar.js的响应体数据返回给浏览器,之所以可以随意发送,是因为每份数据都有对应的ID,浏览器接收到之后,会筛选出相同ID的内容,将其拼接为完整的HTTP响应数据。
HTTP/2使用了多路复用技术,可以将请求分成一帧一帧的数据去传输,这样带来了一个额外的好处,就是当收到一个优先级高的请求时,比如接收到JavaScript或者CSS关键资源的请求,服务器可以暂停之前的请求优先处理关键资源的请求。
多路复用的实现
HTTP/2协议栈从图中可以看出,HTTP/2添加了一个二进制分帧层,结合图分析一下HTTP/2的请求和接收过程。
- 首先,浏览器准备好请求数据,包括了请求行,请求头等信息,如果是POST方法,那么还要有请求体。
- 这些数据经过二进制分帧层处理之后,会被转换为一个个带有请求ID编号的帧,通过协议栈将这些帧发送给服务器。
- 服务器接收到这些帧之后,会将所有相同ID的帧合并为一条完整的请求信息。
- 然后服务器处理该请求,并将处理的响应行,响应头和响应体分别发送至二进制分帧层。
- 同样二进制分帧层将这些数据转换为一个个带有请求ID编号的帧,经过协议栈发送给浏览器。
- 浏览器接收到响应帧之后,会根据ID编号将帧的数据提交给对应的请求。
从以上流程可以看出,通过引入二进制分帧层,就实现了HTTP的多路复用技术
HTTP是浏览器和服务器通信的语言,虽然HTTP/2引入了二进制分帧层,不过HTTP/2的语义和HTTP/1.1依然是一样的,也就是说他们通信的语言并没有改变,比如开发者依然可以通过Accept请求头告诉服务器希望接收到什么类型的文件,依然可以使用Cookie来保持登录状态,依然可以使用Cache来缓存本地文件,这些都没有变,发生改变的只是传输方式。这一点对于开发者来说尤为重要,这意味着我们不需要为HTTP/2去重建生态,并且HTTP/2推广起来相对轻松。
HTTP/2其他特性
多路复用是HTTP/2的最核心功能,它能实现资源的并行传输,多路复用技术是建立在二进制分帧层的基础上,基于二进制分帧层,HTTP/2还附带实现很多其他功能。
1. 可以设置请求的优先级
HTTP/2提供了请求优先级,可以在发送请求时,标上该请求的优先级,这样服务器接收到请求之后,会优先处理优先级高的请求。
2. 服务器推送
HTTP/2可以直接将数据提前推送到浏览器。当用户请求一个HTML页面之后,服务器知道该HTML页面会引用几个重要的JS文件和CSS文件,那么接收到HTML请求之后,附带将要使用的CSS文件和JS文件一并发送给浏览器,这样,当浏览器解析完HTML文件之后,就能直接拿到需要的CSS文件和JS文件,这对首次打开页面速度起到至关重要的作用。
3. 头部压缩
HTTP/2对请求头和响应头进行了压缩。这样传输效率能得到大幅提升。
HTTP/3:甩掉TCP、TLS的包袱,构建高效网络
HTTP/2的缺陷:
TCP的队头阻塞
从上图看出,从一端发送给另一端的数据会被拆分为一个个按照顺序排列的数据包,这些数据包通过网络传输到了接收端,接收端再按照顺序将这些数据包组合成原始数据,这样就完成了传输。
不过如果在数据传输的过程中,有一个数据因为网络故障或者其他原因而丢包了,那么整个TCP的连接就会处于暂停状态,需要等待丢失的数据包被重新传输过来。
我们把在TCP传输过程中,由于单个数据包的丢失而造成的阻塞称为TCP上的队头阻塞。
那么队头阻塞在HTTP/2中中是什么样的呢?如图所示:
HTTP/2多路复用
通过该图看出,在HTTP/2中,多个请求是跑在一个TCP管道中的,如果其中任意一路数据流中出现了丢包的情况,那么就会阻塞该TCP连接中的所有请求。这不同于HTTP/1.1,使用HTTP/1.1时,浏览器为每个域名开启了6个TCP连接,如果其中1个TCP连接发生了队头阻塞,那么其他的5个连接依然可以继续传输数据。
随着丢包率的增加,HTTP/2的传输效率也越来越差。有测试数据表明,当系统达到了2%的丢包率时,HTTP/1.1的传输效率反而比HTTP/2表现的更好。
TCP建立连接的延时
除了TCP队头阻塞,TCP的握手过程也是影响传输效率的一个重要因素。
网络延迟:又称为RTT(Round Trip Time)。我们把从浏览器发送一个数据包到服务器,再从服务器返回数据包到浏览器的整个往返时间称为RTT。RTT是反映网络性能的一个重要指标。
在建立TCP连接的时候需要和服务器进行三次握手来确认连接成功,也就是需要消耗完1.5个RTT之后才能进行数据传输。
而HTTPS需要使用TLS协议进行安全传输,需要更多的RTT。
总之在传输数据之前需要花费3~4个RTT,如果浏览器与服务器的物理距离较近,那么1个RTT的时间可能10ms以内,总共需要30-40ms,如果服务器相隔较远,那么一个RTT就可能需要100ms以上了,这种情况整个握手就需要300-400ms,这时用户就能明显感觉到慢了。
TCP协议僵化
我们知道了TCP协议存在队头阻塞和建立连接延迟等缺点,那么是否可以通过改进TCP协议来解决这些问题呢?
答案是:非常困难。
原因有二:
第一个是中间设备僵化,什么是中间设备?互联网是由多个网络互联的网状结构,为了能够保障互联网的正常工作,我们需要在互联网的各处搭建各种设备,这些设备就被称为中间设备。
这些中间设备有很多种类型,并且每种设备都有自己的目的,这些设备包括了路由器,防火墙,NAT,交换机等。他们通常依赖一些很少升级的软件,这些软件使用了大量的TCP特性,这些功能被设置之后就很少更新了。
所以,如果在客户端升级了TCP协议,但是新协议的数据包经过这些中间设备时,它们可能不理解包的内容,于是这些数据就会被丢弃掉,这就是中间设备僵化。它是阻碍TCP更新的一大障碍。
除了中间设备僵化外,操作系统也是导致TCP协议僵化的另外一个原因。因为TCP协议都是通过操作系统内核来实现的,应用程序只能使用不能修改。通常操作系统的更新都滞后于软件的更新,因此想要更新内核中的TCP协议也是非常困难的。
QUIC协议
HTTP/2存在一些比较严重的与TCP相关的缺陷,但由于TCP协议僵化,不可能修改TCP协议来解决这些问题,那么解决思路就是绕过TCP协议,发明一个TCP和UDP之外的新的传输协议,但是这也面临着修改TCP一样的挑战,因为中间设备的僵化,这些设备只认TCP和UDP,如果采用了新的协议,新协议在这些设备同样不被很好的支持。
因此,HTTP/3选择了一个折中的方法,UDP协议,基于UDP实现了类似于TCP的多路数据流,传输可靠性等功能,我们把这套功能称为QUIC协议。关于HTTP/2和HTTP/3协议栈的比较可以参考下图:
通过上图可以看出,HTTP/3中的QUIC协议集合了一下功能:
-
实现了类似TCP的流量控制、传输可靠性的功能。虽然UDP不提供可靠性的传输,但QUIC在UDP的基础上增加了一层来保证数据可靠性传输。它提供了数据包重传,拥塞控制以及其他一些TCP中存在的特性。
-
集成了TLS加密功能。
-
实现了了HTTP/2中的多路复用功能。和TCP不同QUIC实现了在同一物理连接上可以有多个独立的逻辑数据流。实现了数据流的单独传输,就解决了TCP中队头阻塞问题。
QUIC协议的多路复用
-
实现了快速握手功能基于UDP,所以QUIC可以实现使用0-RTT或者1-RTT来建立连接,这意味着QUIC可以用最快的速度来发送和接收数据,这样可以大大提升首次打开页面的速度。
HTTP/3的挑战
在技术层面HTTP/3是个完美的协议,但是在实际环境中依然面临诸多严峻挑战
- 服务器和浏览器都没有对HTTP/3提供比较完整的支持。
- 部署HTTP/3也存在非常大问题,因为系统内核对UDP的优化远远没有达到TCP的优化程度,这也是阻碍QUIC的一个重要原因。
- 中间设备僵化问题,这些设备对UDP的优化晨读远远低于TCP据统计使用QUIC协议时,大约有3%-7%的丢包率。
小结:HTTP/2存在一些问题,主要包括了TCP队头阻塞,建立TCP连接的延时,TCP协议僵化等问题。
这些问题都是TCP的内部问题,因此解决问题就要优化TCP或者另起炉灶,由于优化TCP存在诸多挑战,所以官方选择创建新的QUIC协议。
HTTP/3正是基于QUIC协议的,QUIC协议可以看做是集成了TCP+HTTP/2的多路复用+TLS等功能的一套协议。这是一个集众家所长的一个协议,从协议最底层对Web的文件传输做了比较彻底的优化,所以等生态成熟时,可以用来打造比HTTP/2还更加高效的网络。
虽然这套协议解决了HTTP/2中因TCP而带来的问题,不过由于是改动了底层协议,所以推动起来会面临巨大挑战。这一点和HTTP/2有本质区别。
网友评论