,6月6日,IETF(互联网工程任务组)正式发布了HTTP/3RFC。HTTPHistory1991HTTP/1.12009Google设计了基于TCP的SPDY2013QUIC2015HTTP/22018HTTP/3HTTP3使用UDP实现高速的同时保持了QUIC的稳定性(选择QUIC就是选择了UDP),同时又不牺牲TLS性的安全性。HTTP2协议虽然在HTTP/1.1的性能上有了很大的提升,但是,基于TCP实现的HTTP2却留下了三个问题:有序字节流造成的Head-of-line阻塞使得HTTP2的多路复用能力大大降低;TCP和TLS叠加了握手延迟,还有1倍建链长度的空间;一个连接是基于TCP四元组确定的,诞生于有线网络设计,不适合移动应用。对于无线网络,这意味着IP地址的频繁更改将导致TCP连接和TLS会话中的重复握手,这是代价高昂的。HTTP3协议解决了这些问题:HTTP3在UDP协议的基础上重新定义了连接,在QUIC层实现了乱序并发的字节流传输,解决了head-of-line阻塞问题(包括解决head-of-lineblockingofdynamictablesbasedonQPACK);HTTP3重新定义了TLS协议加密QUIC头的方式,不仅增加了网络攻击的成本,也降低了连接建立的速度(只需要1个RTT就可以完成)同时建立链路和协商密钥);HTTP3将Packet、QUICFrame和HTTP3Frame分离,实现连接迁移功能,降低5G环境下高速移动设备的连接维护成本。QUIC协议概述QUIC(QuickUDPInternetConnections,快速UDP网络连接)是一种基于UDP的协议,它利用了UDP的速度和效率,同时融合并优化了TCP、TLS和HTTP/2的优点。用一张图就可以清楚的看出它们之间的关系。QUIC是用来替代TCP、SSL/TLS的传输层协议,在传输层之上还有一个应用层。我们熟悉的应用层协议有HTTP、FTP、IMAP等,这些协议理论上都可以运行在QUIC上。QUIC上运行的协议叫做HTTP/3,就是HTTPoverQUIC的意思,即HTTP/3。因此,要想了解HTTP/3,就绕不开QUIC。这里有一些重要的QUIC特性。0RTT建立连接RTT:往返时间??,只包括来回请求访问的时间。HTTP/2的连接建立需要3个RTT。如果考虑会话重用,即缓存第一次握手计算的对称密钥,也需要2个RTT。更进一步,如果TLS升级到1.3,那么HTTP/2连接需要2RTT,考虑会话多路复用需要1RTT。如果HTTP/2不急于HTTPS,可以简化,但实际上几乎所有浏览器的设计都要求HTTP/2需要基于HTTPS。HTTP/3第一次连接只需要1RTT,后面的链接只需要0RTT,也就是说客户端发送给服务端的第一个数据包就包含了请求数据。主要连接过程如下:对于第一次连接,客户端发送InchoateClientHello,用于请求连接;服务器生成g,p,a,根据g,p,a计算出A,然后将g,p,A放入ServerConfig中,并向客户端发送Rejection消息。客户端收到g,p,A后,再次生成b,根据g,p,a计算出B,根据A,p,b计算出初始密钥K,计算出B和K后,客户端将使用K对HTTP数据进行加密,与B一起发送给服务端。服务端收到B后,根据a、p、B生成与客户端相同的密钥,然后用这个密钥对接收到的HTTP数据进行解密。为了进一步的安全(前向安全),服务端会自己更新随机数a和公钥来生成新的密钥S,然后通过ServerHello将公钥发送给客户端。与ServerHello消息一起,还有HTTP返回数据。这里使用DH密钥交换算法。DH算法的核心是服务器产生a、g、p3个随机数,客户端和服务端可以通过DH算法计算出相同的密钥。在这个过程中,a和b不参与网络传输,安全性大大提高。因为p和g是大数,即使在网络传输中,p、g、A、B都是被劫持的,以目前的算力无法破解。连接迁移TCP连接基于四元组(源IP、源端口、目标IP、目标端口)。切换网络时,至少有一个因素会发生变化,导致连接发送发生变化。当连接发送发生变化时,如果是原来的TCP连接,会导致连接失败,必须等到原来的连接超时才能重新建立连接,所以我们有时会发现在切换到新的网络时,即使网络状况良好,内容仍然需要加载很长时间。如果实现良好,当检测到网络变化时,会立即建立新的TCP连接。即便如此,建立新的连接仍然需要数百毫秒。QUIC不受四元组的影响。当这四个元素发生变化时,原来的连接仍然保持。原理是这样的:QUIC不使用四个元素来表示,而是使用一个64位的随机数,称为ConnectionID,即使IP或端口发生变化,只要ConnectionID不变,仍然可以保持连接。HeadOfLineBlocking/MultiplexingHTTP/1.1和HTTP/2都存在行头阻塞(HeadOfLineblocking)的问题。TCP是面向连接的协议,即发送请求后需要收到ACK报文,以确认对象已经接受数据。如果每个请求都需要在收到上一个请求的ACK报文后才去请求,效率无疑是很低的。后来HTTP/1.1提出了Pipeline技术,允许一个TCP连接同时发送多个请求。这提高了传输效率。在这种情况下,会发生队头阻塞。比如一个TCP连接同时发送10个请求,其中1、2、3个请求被客户端接收到,但是第4个请求丢失了,那么后面的5-10个请求都被阻塞。它需要等待第四个请求被处理后才能处理。这浪费了带宽资源。因此,HTTP一般允许每台主机建立6个TCP连接,可以充分利用带宽资源,但每个连接中的队头阻塞问题仍然存在。HTTP/2的多路复用解决了上述的队头阻塞问题。在HTTP/2中,每个请求被拆分成多个Frames,通过TCP连接同时传输,这样即使一个请求被阻塞,也不会影响其他请求。但是,虽然HTTP/2可以解决请求粒度上的阻塞,但是HTTP/2的基础TCP协议也存在队头阻塞的问题。HTTP/2的每个请求都会被拆分成多个Frames,不同请求的Frames组合成一个Stream,Stream是TCP上的一个逻辑传输单元,这样HTTP/2就达到了一次同时发送多个请求的目的连接,其中Stram1已正确传递,而Stram2中的第三帧丢失。TCP以严格的顺序处理数据。首先发送的Frame必须先处理,这将要求发送方重新发送第三个Frame。Steam3、Steam4虽然到了但是不能处理,那么整个链条的路都会被堵死。不仅如此,因为HTTP/2必须使用HTTPS,而HTTPS使用TLS协议也存在队头阻塞的问题。TLS根据Record组织数据,将一对数据一起加密,加密完成后拆分成多个TCP包传输。一般每个Record都是16K,包含12个TCP包,所以如果这12个TCP包中的任何一个丢失,都无法解密整条Record。队头阻塞会导致在更容易丢包的弱网络环境下,HTTP/2比HTTP/1.1慢。QUIC是如何解决队头阻塞问题的?主要有两点:QUIC的传输单位是Packet,加密单位也是Packet。整个加密、传输、解密都是基于Packet,可以避免TLS的阻塞问题。QUIC基于UDP。UDP数据包在接收端不按顺序处理。即使中途丢了一个包,也不会阻塞整个连接。其他资源将正常处理。拥塞控制拥塞控制的目的是避免过多的数据一次性涌入网络,导致网络超过最大负载。QUIC的拥塞控制类似于TCP,并在此基础上进行了改进。我们先来看看TCP的拥塞控制。慢启动:发送方向接收方发送一个数据单元,收到确认后发送2个单元,然后4个单元,8个单元依次呈指数增长。在此过程中,不断测试网络的拥塞程度。避免拥塞:指数增长到一定限度后,指数增长变为线性。快速重传:发送方会为每次传输设置一个超时定时器。超时后认为丢失,需要重传。快速恢复:在上述快速重传的基础上,当发送方重新发送数据时,也会启动一个超时定时器。如果它收到确认消息,它将进入拥塞避免阶段。如果仍然超时,则返回慢启动阶段。QUIC重新实现了TCP协议中的Cubic算法,用于拥塞控制。以下是QUIC改进拥塞控制的特点:1.如果要在热插拔TCP中修改拥塞控制策略,需要在系统层面进行这个操作,而QUIC修改拥塞控制策略只需要运行在应用层,QUIC会根据不同的网络环境和用户动态选择拥塞控制算法。2、前向纠错FECQUIC采用前向纠错(FEC,ForwordErrorCorrection)技术来增加协议的容错能力。将一条数据分成10个包后,每次对每个包进行异或运算,运算结果为由于FEC包是和数据包一起传输的,如果在传输过程中丢失了一个数据包,然后可以根据剩余的9个数据包和FEC数据包计算丢失数据包的数据,这大大增加了协议的容错性。这是一种符合目前网络传输技术的解决方案。在这个阶段,带宽不再是网络传输的瓶颈,往返时间才是。因此,新的网络传输协议可以适当增加数据冗余,减少重传操作。3.单调递增的PackerNumber为了保证可靠性,TCP使用SequenceNumber和ACK来确认消息是否按顺序到达,但这种设计存在缺陷。超时后客户端发起重传,稍后收到ACK确认报文,但是由于原请求和重传请求收到的是同一个ACK报文,客户端不知道ACK对应的是原请求还是重传要求。会造成歧义。RTT:RoundTripTime,往返事件RTO:RetransmissionTimeout,超时重传时间如果客户端认为是重传的ACK,但实际上是右边的情况,RTT会小一些,否则RTT会大一些.QUCI解决了上述歧义问题。与SequenceNumber不同,PacketNumber是严格单调递增的。如果PacketN丢失,重传时Packetidentifier不会是N,而是一个比N大的数,比如N+M,这样发送方在收到确认报文的时候,可以方便的知道原来的请求对应的是不是NACK是重传请求。4.ACKDelayTCP在计算RTT时没有考虑接收方收到数据和发送方确认消息之间的延迟。如下图所示,这个延时就是ACKDelay。QUIC将这种延迟考虑在内,使得RTT的计算更加准确。5.更多的ACK块一般来说,接收方在收到发送方的报文后应该发送一个ACK恢复,表示数据已经收到。但是每次收到一条数据都要返回一个ACK恢复太麻烦了,所以一般不会马上回复,而是收到多个数据后回复。TCPSACK最多提供3个ACK??块。但是在某些场景下,比如下载,只需要服务器返回数据即可,但是根据TCP的设计,每收到3个数据包都需要返回一个ACK,而QUIC最多可以携带256个ACK块。在丢包率比较严重的网络中,增加ACK可以减少重传量,提高网络效率。浏览控制TCP将控制每个TCP连接的流量。流量控制是指发送方发送的速度不能太快,接收方要有时间接受,否则会造成数据溢出和丢失。TCP流量控制主要是通过滑动窗口实现的。实现了。可以看出,拥塞控制主要控制发送方的发送策略,而没有考虑接收方的接收能力。流量控制对某些功能不利。QUIC只需要建立一个连接,同时在这个连接上传输多个Streams,就好比有一条路,每条路都有一个仓库,路上有很多车辆在运送物资。QUIC有两个层次的流控:连接层次(ConnectionLevel)和流层次(StreamLevel)。对于单个Stream的流控:当Stream还没有传输数据时,接收窗口(flowcontrolrecevicewindow)是最大的接收窗口,随着receiver接收到数据,接收窗口不断缩小。在接收到的数据中,有已经处理过的数据,也有还没有处理完的数据。如下图,蓝色方块代表处理后的数据,黄色方块代表违规处理的数据。这部分数据的到来缩小了Stream的接收窗口。随着数据的不断处理,接收方有能力处理更多的数据。当(flowcontrolreceiveoffset-consumedbytes)<(maxreceivewindow/2)满足时,receiver会发送WINDOW_UPDATE帧告诉sender可以继续发送更多的数据,然后flowcontrolreceiveoffset会移位,接收窗口将增加,发送方可以向接收方发送更多数据。Stream级别对于防止接收端接收过多数据作用有限,更有必要使用Connection级别的流控。理解了Stream流之后,也就很容易理解Connection流控了。在流中。接收窗口=最大接受窗口-连接接收数据:接收窗口=Stream1接收窗口+Stream2接收窗口+...+StreamN接收窗口
