从http到QUIC

1,748 阅读10分钟

零、为什么要分享这个主题?

  • chorme浏览器已经支持quic协议(虽然为了安全默认关闭,还是草案中),但quic有望大大降低首次首屏的加载时间,解决困扰我们很久首次渲染的性能问题。
  • http3草案: quicwg.org/base-drafts…

一、http的发展史

首先先来回顾下http的发展历程,了解下http是如何来的。

  • http是在1965年,由Ted Nelson提出的,并在Xanadu项目中实现超文本系统的原型
  • 在1989年,Tim Berners-lee提出了'超文本'(以一种无约束的方式联系起来的人类可读信息)和‘超媒体’(不限于文本),搭建“"通用系统"。
  • 在1991年,就提出http/0.9,但是比较简单,而且只有get方法,没有首部,只能获取html,没有图片和文本
  • 在1996年,提出了http/1.0,这时候就包括首部、响应码、重定向、错误、条件请求、内容编码(压缩)等等。但是不包括共用请求,强制的host首部,缓存的选择
  • 在1999年,提出的http/1.1,这时候就强制host首部,有虚拟主机托管,不用每个请求发起tcp连接。同时也扩展了缓存相关首部、options方法、upgrate首部、Range请求、压缩和传输编码、管道化
  • 在2009年,Google工程师Mike Belshe和Roberto Peon提出了SPDY,在http之前做了一层会话。
  • 在2015年,提出了http/2.0,融合SPDY的优势,使用了二进制分帧层,多路复用,头部压缩,服务器推送等等。。。
  • 以下就是我整理的http发展的时间线

二、http2的特性和详解

  • 1、名词认识:
    • SPDY(读作“SPeeDY”)是Google开发的基于TCP的会话层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。就是在应用层中增加1个SPDY协议,如下图所示,在原本的应用层中,增加了SPDY层。
    • 数据帧:http/2大致分为分帧层和数据层。数据帧第一个是length,之后是帧的类型,flags就带有帧的标识。
    • 流:
  • 2、特性:
    • (1)、二进制传输,原来HTTP/1.x 是以纯文本形式的报文,到了HTTP/2就 采用二进制格式传输数据。如下看两种的区别。
      • 纯文本解析:以纯文本去显示和解析,要不断的去判断增加逻辑,效率非常低。如以下伪代码解析。
      • 二进制解析:解析起来就比纯文本更加方便。如以下伪代码的解析。代码量少,更加方便
      • http/2把原来的"Header+Body"的消息"打散"为数个小片的二进制"帧"(Frame),用"HEADERS"帧存放头数据、"DATA"帧存放实体数据。HTTP/2数据分帧后"Header+Body"的报文结构就完全消失了,协议看到的只是一个个的"碎片"。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。
    • (2)、多路复用
      • http1.1的时候,为了解决这个方法,是将同一页面的资源分散到不同域名下,提升连接上限,Chrome有个机制,对于同一个域名,默认允许同时建立 6 个 TCP持久连接,使用持久连接时,虽然能公用一个TCP管道,但是在一个管道中同一时刻只能处理一个请求,在当前的请求没有结束之前,其他的请求只能处于阻塞状态。另外如果在同一个域名下同时有10个请求发生,那么其中4个请求会进入排队等待状态,直至进行中的请求完成。
      • 多路复用很好的解决了浏览器限制同一个域名下的请求数量的问题,同时也更容易实现全速传输,毕竟新开一个 TCP 连接都需要慢慢提升传输速度。
      • 效果如下:
      • 传输的原理:http/2中同域名下所有通信都在单个连接上完成。单个连接可以承载任意数量的双向数据流。数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。
    • (3)、头部压缩
      • 通过Hapck算法,在客户端和服务器两端建立“字典”,通过索引去除重复请求,查找压缩方案。
      • 使用霍夫曼编码,能接近gzip的压缩率,也能压缩字符串
      • 例如下图中的两个请求,请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销
    • (4)、服务器推送(Server push)
      • 在 HTTP/2.0 中,服务器可以向客户发送请求之外的内容,比如正在请求一个页面时,服务器会把页面相关的 logo,CSS等文件直接推送到客户端,而不会等到请求来的时候再发送,因为服务器认为客户端会用到这些东西。这相当于在一个 HTML 文档内集合了所有的资源。如下图所示:
      • 服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送RST_STREAM帧来拒收。主动推送也遵守同源策略,换句话说,服务器不能随便将第三方资源推送给客户端,而必须是经过双方确认才行。
    • (5)、加密传输
      • 因为主流的浏览器Chrome、Firefox等都公开宣布只支持加密的HTTP/2,所以“事实上”的HTTP/2是加密的。也就是说,互联网上通常所能见到的HTTP/2都是使用"https”协议名,跑在TLS上面。HTTP/2协议定义了两个字符串标识
    • (6)、优先级
      等等
  • 3、http2时代和http1不同的优化方法:
    • (1)、http1使用Spriting合并多张小图为一张大图,再用JavaScript或者CSS将小图重新“切割”出来的技术。在http2因为多路复用的应用就不需要这么做了。
    • (2)、http1.1时代使用内联(Inlining)去防止发送很多小图请求的技巧,将图片的原始数据嵌入在CSS文件里面的URL里,减少网络请求次数。
    • (3)、在http1.1时代要拼接(Concatenation)将多个体积较小的JavaScript使用webpack等工具打包成1个体积更大的JavaScript文件,但如果其中1个文件的改动就会导致大量数据被重新下载多个文件。,在http/2时代也不需要这么做,可以分很多js文件去加载。
    • (4)、http1.1时代使用多域名去实现假并行提高传输效率,在http2时代就不需要使用多域名,一个域名建立tcp连接后就可以多路复用去传输。

三、http2存在的问题

  • 1、HTTP/2都是基于TCP去实现的,但是当数据发生丢包的时候会发生阻塞(即tcp hol Blocking)进行数据重传,继而触发“拥塞发生”,其拥塞窗口降为1,在未收到ack之前其他包阻塞。如下图所示:
  • 2、在弱网情况下,频繁丢包,http2的多路复用反而比http1更低效。因为TCP为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,HTTP/2出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该TCP连接中的所有请求。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反倒只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。
    • http2弱网络测试
  • 3、tcp连接时先要进行3次握手来确认连接成功,也就是需要至少消耗1.5个RTT才能进行数据传输。
  • 4、使用https时,还需要TLS协议进行安全传输,使用TLS也需要一个握手过程。TLS有2个版本,TLS1.2和TLS1.3,每个版本建立连接所花的时间也不同,大致需要1~2个RTT。SSL进行中,要进行公钥和私钥传输,如下图:

四、QUIC的介绍和使用

Google设计了一种新的协议QUIC,让HTTP跑在UDP上而不是TCP上。

  • (1)定义:QUIC(Quick UDP Internet connections)快速udp网络连接,由谷歌发明的。顾名思义,是基于UDP做传输层,而与之前的tcp做传输层有区别。
  • (2)原理:使用UDP实现一个可靠的多路复用传输层。UDP是面向数据报文的,数据包之间没有阻塞约束,QUIC就是利用这个特性解决传输层的对头阻塞问题。
  • (3)功能
    • 【1】引入了类似HTTP/2的“流”和“多路复用”,单个“流"是有序的,可能会因为丢包而阻塞,但其他“流”不会受到影响,彻底解决TCP中队头阻塞的问题。

    • 【2】实现了类似TCP的流量控制、传输可靠性的功能,QUIC在UDP的基础之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些TCP中存在的特性。以下是TCP、UDP和QUIC的区分:

    • 【3】实现了快速握手功能: 0 Rtt

      • 使用UDP规避了三次握手
      • 使用DH加密来规避TLS交互
      • 首包就可以发送数据
      • 而且有1次Server Config的缓存,后面只要缓存不失效,重连也无需TLS交互。
    • 【4】可靠性:

      • 方法:
        • (1)、单调递增seq
        • (2)、单向冗余纠错(异或)
        • (3)、失败重传进行处理
      • 当发生丢包时
        • (1)、尝试使用前向纠错来修复
        • (2)、不能fec,则失败重传。

      举个例子:当发送数据A和B,增加发送1个数据C等于A和B的异或,接收方接到这3个包的任意2个包,异或1下就可以得到第3个包

    • 【5】、quic拥塞窗口:

      • 实现了tcp的Cubic和NewReno算法
      • 默认采用Cubic
      • nack机制,由接收端告知哪几个包丢失
      • Tail Loss Probes更及时的重传
    • 【6】、其他

      • 连接性:使用connection id来识别重新连接的请求
      • 流量控制:可以针对连接控制,也可以对具体的stream id进行控制
  • (3)优势:
    • 【1】、效率高,传输快
    • 【2】、谷歌调优后的QUIC更是重建了可靠性和有序性,但减少了连接次数,尤其是对于加密连接来说,能够使用先前协商过的相同加密恢复旧连接,而不需要任何额外的往返。
    • 【3】、QUIC 可以实现可靠传输,而且相比于 TCP,它的流控功能在用户空间而不在内核空间,那么使用者就 不受限于 CUBIC 或是 BBR,而是可以自由选择,甚至根据应用场景自由调整优化。
  • (4)quic支持状况:
    • 运营商:udp降级,在国内部分地区的运营商都会针对 UDP 协议QOS限速或者丢包,这就导致 UDP 效率低下,或许速度会比正常使用TCP协议还慢很多。
    • chrome支持:通过chrome://flags/#enable-quic控制,显示quic的协议,看到Experimental QUIC protocol改成enabled,重启浏览器就可以看到,不过要这个网站支持quic才行。
    • nginx暂不支持

五、迎接http3的来临

QUIC 基于 UDP 实现,是 HTTP/3 中的底层支撑协议,该协议基于 UDP,又取了 TCP 中的精华,实现了即快又可靠的协议。也许http3普及之后,现在很多做的性能优化操作就不需要了。