- 注意:
- NGINX的QUIC分支仍处于早期开发阶段,特殊需求较多且不稳定,不建议在生产环境中折腾。
- QUIC属于较新颖的技术,相关协议和项目变化很快,本文叙述的内容在发布时是最新但可能很快也会过时,请各位在安装时注意查证,尽量安装各项目的最新版。
- 本文目录:
- QUIC和HTTP/3技术的简介
- 部署环境和采用的项目说明
- 编译安装Camke
- 编译安装Perl
- 安装Golang
- 编译安装GCC
- 编译安装Boringssl
- 编译安装Nginx-Quic
- 配置文件编写与测试
- QUIC现存的问题和前景
- Quic和HTTP/3技术简介
QUIC,全称为 Quick UDP Internet Connection,最初由Google的一名工程师 Jim Roskind 设计编写,并于2012年成功部署。2013年Google将这个草案向IETF提交,次年正式公布草案,2018年IETF将QUIC草案正式更名为HTTP/3,确立其为下一代HTTP标准。2019年,Chrome和Firefox支持了HTTP/3,2020年Cloudflare及其他几家公司陆续将HTTP/3投入测试,2020年底Cloudfalre正式公测HTTP/3。
与现行的最新版正式案HTTP/2不同,HTTP/3放弃了被形容为是一个 “千疮百孔的烂摊子” 的TCP协议,转而使用了基于UDP魔改而来的QUIC协议,希望能够彻底解决HTTP诞生以来就一直阴魂不散的队头阻塞和受限于TCP流控的问题。
要了解QUIC的诞生原因,我们要先了解一下TCP的问题。
首先便是队拥塞,如下图所示,在一个TCP连接中,当一个编号靠前的包出现丢失时,从用户的视角来看,整个传输就像忽然断流了一样。因为在TCP中,包必须按顺序发送给用户,所以即使内核已经收到了后续的包,在先前的包没有到达的情况下,这些包不能交给用户,所以在用户看来,这个TCP连接就像是突然被掐断了一样。
其次便是冗余传输,在一个TCP连接建立时,即使是两个已知的主机也不得不进行TCP的三次握手,这导致了一定程度的传输浪费,因为这完全是不必要的行为。
最后便是TCP不支持流级的连接复用。尽管在HTTP/2中,连接复用在帧层面上实现了,但是对于TCP来说,它仍然不知道发生了什么,所以最上面说的那个缺点依旧存在,而HTTP/2解决的其实是仅用一个TCP连接来传输多个HTTP请求的问题而已,并不能杜绝队头拥塞的出现。
在了解了TCP的缺点之后,QUIC的优点也就相应的很好理解了。
- QUIC在流层面启用了多路复用,杜绝了一个数据包堵住一整个连接的问题。
2. QUIC使用了更新过的错误纠正和丢包补发机制,对于丢包率较高或连接不稳定的连接情况提供了更好的体验。
3. QUIC使用session作为连接恢复的凭证,但与TCP不同的是,QUIC在恢复连接时不要求请求者的IP一致,仅需要一个正确的凭证即可恢复连接,对于从WIFI切换到移动数据等连接变换场景很有用。同时,QUIC在恢复连接时无需进行握手,可直接开始传输,仅需将session附在第一个请求中即可。
4. QUIC对于TLS的兼容性更好,在双向延迟100ms的假设下,与HTTP/2+TLS1.2的组合中第一次握手300ms,后续200ms相比,HTTP/3+TLS1.3的组合在第一次握手时只需要100ms,后续更是无需进行握手,将冗余的连接降到了最少,充分利用了传输的带宽,降低了延迟。
5. 除此之外,HTTP/3还使用了一种新的HTTP头压缩机制,称为QPACK,是对HTTP/2中使用的HPACK的增强。在QPACK下,HTTP头可以在不同的QUIC流中不按顺序到达。与HTTP/2中的TCP确保数据包的按顺序传递不同,QUIC流是不按顺序传递的,在不同的流中可能包含不同的HTTP头。因此,QPACK改为使用查找表机制对报头进行编码和解码。
在了解了QUIC的原理和优势之后,我们来部署基于Nginx-QUIC的HTTP/3服务器
- 部署环境和项目介绍
服务器系统:Centos7.6
服务器内核:Kernel-5.10.2-BBR
QUIC项目:Nginx-QUIC
HTTP/3的实现方式有很多,诸如Cloudflare的quiche 、Caddy的实验分支等,在这里我选用的是Nginx官方的QUIC分支。接下来的步骤假设的是你使用的是全新安装的Centos7系统,如果自行升级过软件包请适当做调整。
- 编译安装Camke
首先我们要安装一下一个套件 “Development Tools” ,其中包含了基础的编译工具。注意网上有些教程会让你装 build-essential ,这个套件是Ubuntu上的,无法在Centos上安装。
yum update -y
#更新软件包列表和已有软件包
yum group install "Development Tools"
#安装套件
接下来我们安装Cmake,Cmake的官网是 https://cmake.org/download/
安装时请务必前往官网复制最新版本的下载链接,不要照抄指令。
wget https://github.com/Kitware/CMake/releases/download/v3.21.3/cmake-3.21.3.tar.gz
#请自行替换为最新版本链接
tar -zxvf cmake-3.21.3.tar.gz
cd cmake-3.21.3
#请自行将包名换成你下载的版本
./bootstrap
gmake
make && make install
注:如果在运行 ./bootstrap 时提示缺少软件包的话请自行安装。完成后请务必检查Cmake版本。
cmake --version
#示例输出:
#cmake version 3.21.3.
#CMake suite maintained and supported by Kitware (kitware.com/cmake).
- 编译安装Perl
Perl的版本需要尽可能新,否则容易出问题,我当时使用的是5.34.0。
Perl的官网是 http://www.cpan.org/src/
安装时请务必前往官网复制最新版本的下载链接,不要照抄指令。
wget https://www.cpan.org/src/5.0/perl-5.34.0.tar.gz
tar -zxvf perl-5.34.0.tar.gz
cd perl-5.34.0
./Configure -des -Dprefix=/usr/local/perl
make -j4 && make test && make install
安装完成后请务必检查Perl版本
perl -v
#示例输出:
#This is perl 5, version 34, subversion 0 (v5.34.0) built for x86_64-linux
#Copyright 1987-2021, Larry Wall
如果输出的版本和你下载的版本不符合,说明系统原来带有Perl,需要进行替换。
# 查看默认的perl
which perl | xargs file
# 替换新安装的perl和原来的perl,注意请将指令中的目录改为上面指令输出的目录
mv /usr/bin/perl /usr/bin/perl.5.16.3
# 需要注意新安装的perl目录要根据前面编译的时候指定的目录来确定,如果你是抄的指令则无需更改
ln -s /usr/local/perl/bin/perl /usr/bin/perl
# 再次检查
perl -v
- 安装Golang
Golang的安装较简单,直接下载解压即可。注意如果系统原来带有Go的话需要替换
Golang的官网是:https://golang.org/dl/
安装时请务必前往官网复制最新版本的下载链接,不要照抄指令。
wget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz
tar -zxvf go1.17.1.linux-amd64.tar.gz -C /usr/local
# 修改系统默认的go文件
ln -s /usr/local/go/bin/go /usr/bin/go
接下来的go环境变量大家可以根据自己的实际需求进行配置。对于我个人而言,我直接在 /etc/profile
中添加下面的配置然后source生效即可。
export GOROOT=/usr/local/go
export GOBIN=$GOROOT/bin
export PATH=$PATH:$GOBIN
export GOPATH=/home/gopath
#环境变量请根据实际情况配置,如果没有特殊要求可以使用上方指令
- 编译安装GCC
CentOS7默认的Gcc的版本太旧了,接下来编译Boringssl 和 Nginx 的时候会报错,需要手动编译安装新版本的GCC,这里我使用的是11.2.0的版本。Gcc的版本可以在这里找到 http://ftp.gnu.org/gnu/gcc/
如果大家之前已经编译安装过GCC-10.0.0 以上的版本则无需再次编译。可以跳过这个步骤,10.0.0 以下的版本没有测试,建议还是安装最新版。
安装时请务必前往官网复制最新版本的下载链接,不要照抄指令。
wget http://ftp.gnu.org/gnu/gcc/gcc-11.2.0/gcc-11.2.0.tar.gz
tar -zxvf gcc-11.2.0.tar.gz
cd gcc-11.2.0
# 解压完成后需要下载四个依赖,我们执行脚本即可直接下载,服务器带宽较小或网络不好的同学可以手动下载后上传到GCC目录内
# 下载链接在此段末尾
./contrib/download_prerequisites
# 创建一个专门用来编译的目录
mkdir gcc-build-11.2.0
cd gcc-build-11.2.0/
# 这里需要对c和c++进行支持,为了节省时间禁用掉了交叉编译,如果有特殊需要的同学把最后的"-disable-multilib"删掉即可
/root/gcc-11.2.0/configure -enable-checking=release -enable-languages=c,c++ -disable-multilib
# 接下来这里需要很久
make -j4
&& make install
注意:编译安装GCC需要大量时间和资源,请务必确认你的服务器资源充足,CPU可以长时间占用且内存大于等于2GB,否则建议在本地完成编译后将编译好的文件(gcc和c++)上传至服务器替换掉原来的 cc 和 c++ 文件。
# 安装完成之后我们需要替换原来的cc文件和c++文件,确保它们的版本都是最新的版本
# 一般来说原来系统的cc文件和c++文件都在/usr/bin/目录下,而我们编译安装的cc文件和c++文件在/usr/local/bin/
cd /usr/bin/
mv /usr/bin/cc /usr/bin/cc.4.8.5
mv /usr/bin/c++ /usr/bin/c++.4.8.5
ln -s /usr/local/bin/gcc /usr/bin/cc
ln -s /usr/local/bin/c++ /usr/bin/c++
# 最后检查版本
gcc -v
#示例输出:
#使用内建 specs
#COLLECT_GCC=gcc
#COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/11.2.0/lto-wrapper
#目标:x86_64-pc-linux-gnu
#配置为:/root/gcc-11.2.0/configure -enable-checking=release -enable-languages=c,c++ -disable-multilib
#线程模型:posix
#Supported LTO compression algorithms: zlib
#gcc 版本 11.2.0 (GCC)
这里也放一下4个GCC编译的依赖包,有需要的同学可以自行下载
#下载后上传至GCC目录下并解压即可
#仅适用于网络不好的状况,其他情况请按上方方法运行脚本
https://nannan-share.oss-cn-shanghai.aliyuncs.com/GCC%E7%BC%96%E8%AF%91%E4%BE%9D%E8%B5%96/gmp-6.1.0.tar.bz2
https://nannan-share.oss-cn-shanghai.aliyuncs.com/GCC%E7%BC%96%E8%AF%91%E4%BE%9D%E8%B5%96/isl-0.18.tar.bz2
https://nannan-share.oss-cn-shanghai.aliyuncs.com/GCC%E7%BC%96%E8%AF%91%E4%BE%9D%E8%B5%96/mpc-1.0.3.tar.gz
https://nannan-share.oss-cn-shanghai.aliyuncs.com/GCC%E7%BC%96%E8%AF%91%E4%BE%9D%E8%B5%96/mpfr-3.1.6.tar.bz2
- 编译安装Boringssl
由于Openssl官方尚未支持QUIC协议,给出的理由是他们仍然忙于开发Openssl-3.0 并且 QUIC 更新太快,所以我们需要使用Google开发的一个分支Boringssl
Google官方建议我们使用Ninja编译安装Boringssl,所以我们先装一个Ninja
wget https://github.com/ninja-build/ninja/releases/download/v1.10.1/ninja-linux.zip
unzip ninja-linux.zip
cp -r ninja /usr/bin/
#检查版本
which ninja
#示例输出:
#/usr/bin/ninja
接下来我们安装Boringssl,如果没有安装Git请先安装。
# 注意这里的git仓库很大,大概在250MB左右,请确保编译安装服务器的网络良好
git clone https://github.com/google/boringssl.git
cd boringssl/
# 建立一个专门用于编译的文件夹
mkdir build
cd build
#开始编译
cmake -GNinja ..
ninja
#注意在执行cmake这一步的时候正常情况下检测到的gcc文件和perl库版本应该是我们之前编译安装好的新版本,如果不对的话需要再次检查
- 编译Nginx-Quic
剩下的nginx的编译安装步骤就和正常的nginx编译安装基本一致,只是需要额外的参数来开启HTTP/3支持并使用Boringssl替代默认的Openssl
Nginx-Quic分支的官网是:hg.nginx.org/nginx-quic
由于仍在开发所以没有稳定版本,前往官网后找到左侧栏中的“tar”按钮,点击下载后重命名为 nginx-quic.tar.gz 后上传到服务器即可。
#假设你已经上传nginx-quic.tar.gz
tar -zxvf nginx-quic.tar.gz
cd nginx-quic
#请根据自身需求更改下列指令参数,下方只是一个最基础的版本
./auto/configure --prefix=/www/nginx --with-http_ssl_module --with-http_v2_module --with-http_v3_module --with-http_gzip_static_module --with-cc-opt="-I../boringssl/include" --with-ld-opt="-L../boringssl/build/ssl -L../boringssl/build/crypto"
#最后编译即可
make && make install
- 编写配置文件并进行测试
注意:
- 由于HTTP/3需要使用443的UDP协议端口,请注意开放对应的防火墙
- HTTP2监听的是443的TCP端口,而HTTP3监听的是UDP端口
- Nginx-Quic中添加了$http3和$quic变量,可以添加到日志中,这样就可以看到是否使用了HTTP/3来进行访问了
- 如果有多个server_name,在不指定IP的情况下,只需要在任意一个配置了 “listen 443 http3 quic reuseport;” 那么其他所有server_name都会开启HTTP3,其他域名不需要再添加该配置否则会报错,如果需要部分server_name开启HTTP/3,请指定监听IP。
- Nginx-Quic增加了http3_max_field_size、http3_max_table_capacity、http3_max_blocked_streams、http3_max_concurrent_pushes、 http3_push、http3_push_preload这六个变量来控制HTTP3的性能
- 如果要启用HTTP/3,则必须配置SSL证书并启用TLS1.3版本。
以下是一个最基础的配置文件,请根据自己的需要进行更改
server {
listen 80;
listen 443 ssl http2;
listen 443 http3 quic reuseport;
# UDP listener for QUIC+HTTP/3
server_name blog.nannan.cool;
#以下为TLS配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ecdh_curve X25519:P-256:P-384;
ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:EECDH+CHACHA20:EECDH+AES128;
ssl_certificate /www/nginx/ssl/blog_nannan/certificate.pem;
ssl_certificate_key /www/nginx/ssl/blog_nannan/private.key;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
# 该选项用于开启address validation,但是会和下方的0-RTT冲突
#quic_retry on;
# 开启 TLS 1.3 0-RTT
ssl_early_data on;
# 添加 Early-Data 头告知后端, 防止重放攻击
proxy_set_header Early-Data $ssl_early_data;
# 参考nginx官方目前支持的http3版本,我们添加对应的header
add_header Alt-Svc 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"';
#HTTP_TO_HTTPS_START
if ($server_port !~ 443){
rewrite ^(/.*)$ https://$host$1 permanent;
}
#HTTP_TO_HTTPS_END
}
测试:
当你修改完配置文件后,请重启你的Nginx服务,然后就可以前往 Geekflare 的 HTTP3 测试工具去测试你的网站啦!
Geekflare测试工具地址:https://gf.dev/http3-test
以下是我的博客测试的结果,如果你按照上面的方法进行了配置的话,应该也是可以成功的。
- QUIC现存的问题和发展前景
QUIC面临的最大挑战就是运营商对于UDP包的劣化和审查,因为众所周知的原因,中国境内的UDP连接往往被干扰甚至掐断,这严重阻碍了QUIC的发展。此外,几乎所有的网络设备都需要升级才能满足QUIC普及后的UDP传输需求。但希望也不是没有,考虑到Google在早期QUIC实验所显示的积极结果,人们对HTTP/3的支持是压倒性的,这将最终迫使中间层厂商标准化。而随着IETF正在进行的标准化工作,这些问题最终都会得到解决。
对于未来发展迅速的IoT设备而言,QUIC是一个很好的选择,但HTTP/3不是。HTTP/3由于过于繁琐而不能应用在许多RAM和CPU功率都有限的IoT设备上。但为了使IoT设备在电池功率、低比特率和有损连接等限制条件下高效运行,QUIC是必须的要求。HTTP/3在现有的UDP之上,以QUIC的形式在传输层处理,增加了HTTP/3在整个协议栈中的占用空间,这使得HTTP/3较为笨重,不适合那些IoT设备。但是新的基于QUIC,专门为小型IoT设备设计的轻量化协议也在诞生,例如MQTT等,这避免了在IoT设备上支持HTTP协议带来的功耗增加和安全隐患。
QUIC还可以显著改善在移动设备和无线连接上的网络质量。TCP是为了有线连接而诞生的,其并没有考虑在无线有损环境下传输的问题,也不再适应当下社会如此庞大的传输流量。Google的一些初步实验证明,QUIC作为Google部分热门服务的底层传输协议,极大地提高了速度和用户体验。部署QUIC作为YouTube视频的底层传输协议,YouTube视频流的缓冲率平均下降了30%,这直接改善了用户的视频观看体验。在显示谷歌搜索结果时,也有类似的提高。
基于这些早期的试验所展现出的改进,我们不难看出,QUIC已经成为带领万维网走向未来的重要因素。在QUIC的支持下,HTTP从HTTP/2到HTTP/3的改头换面,朝着这个方向合理地迈出了一步。
完
参考链接:
《深入解读HTTP3的原理及应用》– 知乎 – 作者:方圆
最后编译nginx出现 ,我理解是版本问题嘛
./auto/configure: error: invalid option “–with-http_v3_module”
请在这里下载 Nginx-Quic 分支的源码:hg.nginx.org/nginx-quic ,你可能使用的是主分支的。主分支还不支持 HTTP/3。