一、QUIC概述

1.1什么是QUIC?

        简单来说,QUIC (Quick UDP Internet Connections) 是Google提出的一种基于 UDP 封装的安全可靠的传输协议(transport protocol),其目标是取代 TCP 并自包含 TLS 成为标准的安全传输协议。既拥有UDP的实时高效性,又提供像TCP一样的可靠性。下图是 QUIC 在TCP/IP协议栈中的位置,主要工作在传输层,基于 QUIC 承载的 HTTP 协议进一步被标准化为 HTTP3.0。

        QUIC是由Google最早提出并在chromium实现,而现今交给IETF推进标准化工作。所以当前QUIC有两大分支流派,一种是基于Google的Chromium工程的QUIC实现,也叫做gQUIC;另外一种是标准化进行中的版本也叫做iQUIC。目前两者由于协议格式、加密方式等等的不同而不能互通。当然从现时QUIC协议开源库支持度、稳定性和多平台兼容来看,比较建议采用Google主导的gQUIC,并且gQUIC在后续也会打通与iQUIC的兼容。

 

 

1.2 为什么选择QUIC?

在 QUIC 出现之前,在传输层,TCP 承载了 90% 多的互联网流量,那又为何会出现革命者 QUIC 呢?这主要是因为发展了几十年的 TCP 面临 “协议僵化问题”,由于TCP本身的问题(一个充满补丁的丑陋的协议),成为了限制web应用性能的一个瓶颈,表现在几方面:

  1. 网络设备支持 TCP 时的僵化,表现在:对于一些防火墙或者 NAT 等设备,如果 TCP 引入了新的特性,比如增加了某些 TCP OPTION 等,可能会被认为是攻击而丢包,导致新特性在老的网络设备上无法工作。

  2. 网络操作系统升级困难导致的 TCP 僵化,一些 TCP 的特性无法快速的被演进。

  3. 除此之外,当应用层协议优化到 TLS1.3、 HTTP2.0 后, 传输层的优化也提上了议程,QUIC 在 TCP 基础上,取其精华去其糟粕具有如下的硬核优势:

        近三十年来,这几个协议的发展都非常缓慢。TCP 主要是拥塞控制算法的改进,SSL/TLS 基本上停留在原地,几个小版本的改动主要是密码套件的升级,TLS1.3是一个飞跃式的变化,但还未正式发布。IPv4 虽然有一个大的进步,实现了 IPv6,DNS 也增加了一个安全的 DNSSEC,但和 IPv6 一样,部署进度较慢。

        随着移动互联网快速发展以及物联网的逐步兴起,网络交互的场景越来越丰富,网络传输的内容也越来越庞大,用户对网络传输效率和 WEB 响应速度的要求也越来越高。一方面是历史悠久使用广泛的古老协议,另外一方面用户的使用场景对传输性能的要求又越来越高。

 

二、QUIC的优势

2.1握手建连更快

        QUIC建连时间大约0~1 RTT,在两方面做了优化:

1)传输层使用了UDP,减少了1个RTT三次握手的延迟。

2)加密协议采用了TLS 协议的最新版本TLS 1.3,相对之前的TLS 1.1-1.2,TLS1.3允许客户端无需等待TLS握手完成就开始发送应用程序数据的操作,可以支持1 RTT和0RTT。

        对于QUIC协议,客户端第一次建连的握手协商需1-RTT,而已建连的客户端重新建连可以使用之前协商好的缓存信息来恢复TLS连接,仅需0-RTT时间。因此QUIC建连时间大部分0-RTT、极少部分1-RTT,相比HTTPS的3-RTT的建连,具有极大的优势。

2.2避免队首阻塞的多路复用

        QUIC同样支持多路复用,相比HTTP/2,QUIC的流与流之间完全隔离的,互相没有时序依赖。如果某个流出现丢包,不会阻塞其他流数据的传输和应用层处理,所以这个方案并不会造成队首阻塞。

2.3支持连接迁移

        什么是连接迁移?举个例子,当你用手机使用蜂窝网络参加远程会议,当你把网络切换到WLAN时,会议客户端会立马重连,视频同时出现一瞬间的卡顿。这是因为,TCP采用四元组(包括源IP、源端口、目标地址、目标端口)标识一个连接,在网络切换时,客户端的IP发生变化,TCP连接被瞬间切断然后重连。连接迁移就是当四元组中任一值发生变化时,连接依旧能保持,不中断业务。QUIC支持连接迁移,它用一个(一般是64位随机数)ConnectionID标识连接,这样即使源的IP或端口发生变化,只要ConnectionID一致,连接都可以保持,不会发生切断重连。

2.4可插拔的拥塞控制

QUIC是应用层协议,用户可以插拔式选择像Cubic、BBR、Reno等拥塞控制算法,也可以根据具体的场景定制私有算法。

2.5前向纠错(FEC)

QUIC支持前向纠错,弱网丢包环境下,动态的增加一些FEC数据包,可以减少重传次数,提升传输效率。

 

三、在 Nginx 中支持 HTTP3.0 / QUIC

3.1方案选择

        HTTP 3.0,也称作 HTTP over QUIC。核心是 QUIC (读音quick)协议,由 Google 在 2015 年提出的 SPDY v3 演化而来的新协议,传统的 HTTP 协议是基于传输层 TCP 的协议,而 QUIC 是基于传输层 UDP 上的协议,可以定义成:HTTP3.0 基于 UDP 的安全可靠的 HTTP2.0 协议,主要有以下特性:

图片来自Nginx官博 

  • 基于 UDP 减少了 TCP 三次握手及 TLS 握手时间

  • 解决多路复用丢包时的线头阻塞问题

  • 优化重传策略

  • 流量控制

  • 连接迁移

接下来说明一下如何在 Nginx 中开启 HTTP3.0 的支持。

3.2改造过程

        最终的目的是得到 nginx-quic 版本的 nginx 可执行文件,需要经过一系列的安装和编译,期间可能会遇到很多问题,如果各位读者不想实际操作,可以直接用其他开发者编译好的开源版本 nginx-quic.linux-x86_64.zip。

准备工作:

        以 centos7 为例,下载 nginx-quic 源码传送门,下载完成之后,需要进行编译安装,由于 nginx-quic 依赖 boringSSL,所以还需下载 boringSSL 源码传送门,然后同样需要编译安装 boringSSL,执行这些操作之前,需要在 linux 上安装一些前置模块,通过 yum 来安装,执行以下命令:

sudo yum install build-essential mercurial psmisc lsb-release cmake golang libunwind-dev git libpcre3-dev zlib1g-dev

 什么是 boringSSL:

        对于 Nginx 来说,在编译时需要配置对于的 SSL 库,不管是 HTTP3.0 还是 HTTP2.0,始终都要基于 HTTPS,而加密算法这块主要有 OpenSSL来提供,而 BoringSSL 是谷歌创建的 OpenSSL 分支,用于支持 TLS1.3的 UDP 协议 0-RTT 数据传输的加密算法(可以理解成 TLS 1.3 是标准协议,BoringSSL 是实现工具),BoringSSL 的一些特性会在合适的时机同步给 OpenSSl。

编译安装 boringSSL:

cd boringssl-master/
mkdir build
cd build
cmake ../
make
build/cryptobuild/ssl

编译安装 nginx-quic:

cd nginx-quic/
./auto/configure --prefix=/root/nginx --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-cc-opt="-I../boringssl-master/include" --with-ld-opt="-L../boringssl-master/build/ssl -L../boringssl-master/build/crypto"
make 
make install
/root/nginxconf/sbin/

修改配置文件,启动 nginx:

vi /root/nginx/conf/nginx.conf

添加 http3 配置:

server {
    listen 443 ssl http2;              # TCP listener for HTTP/2
    listen 443 http3 reuseport;  # UDP listener for QUIC+HTTP/3

    ssl_protocols       TLSv1.3; # QUIC requires TLS 1.3
    ssl_certificate     ssl/www.example.com.crt;
    ssl_certificate_key ssl/www.example.com.key;

    add_header Alt-Svc 'quic=":443"; h3-27=":443";h3-25=":443"; h3-T050=":443"; h3-Q050=":443";h3-Q049=":443";h3-Q048=":443"; h3-Q046=":443"; h3-Q043=":443"'; # Advertise that QUIC is available
}
add_header Alt-Svc

3.3如何测试 QUIC 是否生效

        在文章中已经提过,使用 QUIC 协议需要客户端和服务端支持,如果已经部署到 quic 协议,客户端也已经实现,则可以使用如下几种方案检查是否 quic 生效:

WireShark

 

2.使用检查工具http3-test

 

3. 使用检查工具HTTP/3 Test

由于目前浏览器对HTTP3.0/QUIC的支持性有限,可以通过 http3check.net/来验证站点启用HTTP3是否成功。

 

3.4QUIC 版本

cronetQUICimplementation 'com.google.android.gms:play-services-cronet:16.0.0'
constexpr std::array<QuicTransportVersion, 6> SupportedTransportVersions() {
  return {QUIC_VERSION_IETF_DRAFT_29,
          QUIC_VERSION_IETF_DRAFT_27,
          QUIC_VERSION_51,
          QUIC_VERSION_50,
          QUIC_VERSION_46,
          QUIC_VERSION_43};
}
IETFQUICGoogle QUIC
QUIC_VERSION_43 = 43,  // PRIORITY frames are sent by client and accepted by
                         // server.
  // Version 44 used IETF header format from draft-ietf-quic-invariants-05.
  // Version 45 added MESSAGE frame.
  QUIC_VERSION_46 = 46,  // Use IETF draft-17 header format with demultiplexing
                         // bit.
  // Version 47 added variable-length QUIC server connection IDs.
  // Version 48 added CRYPTO frames for the handshake.
  // Version 49 added client connection IDs, long header lengths, and the IETF
  // header format from draft-ietf-quic-invariants-06
  QUIC_VERSION_50 = 50,  // Header protection and initial obfuscators.
  QUIC_VERSION_51 = 51,  // draft-29 features but with GoogleQUIC frames.
  // Number 70 used to represent draft-ietf-quic-transport-25.
  QUIC_VERSION_IETF_DRAFT_27 = 71,  // draft-ietf-quic-transport-27.
  // Number 72 used to represent draft-ietf-quic-transport-28.
  QUIC_VERSION_IETF_DRAFT_29 = 73,  // draft-ietf-quic-transport-29.
  // Version 99 was a dumping ground for IETF QUIC changes which were not yet
  // yet ready for production between 2018-02 and 2020-02.