Nginx支持HTTP/3(QUIC)折腾记
本文发布于 945 天前,其中的部分信息可能已经失效或过时。
  • 注意:
  1. NGINX的QUIC分支仍处于早期开发阶段,特殊需求较多且不稳定,不建议在生产环境中折腾。
  2. QUIC属于较新颖的技术,相关协议和项目变化很快,本文叙述的内容在发布时是最新但可能很快也会过时,请各位在安装时注意查证,尽量安装各项目的最新版。
  • 本文目录
  1. QUIC和HTTP/3技术的简介
  2. 部署环境和采用的项目说明
  3. 编译安装Camke
  4. 编译安装Perl
  5. 安装Golang
  6. 编译安装GCC
  7. 编译安装Boringssl
  8. 编译安装Nginx-Quic
  9. 配置文件编写与测试
  10. 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的优点也就相应的很好理解了。

  1. 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
  • 编写配置文件并进行测试

注意:

  1. 由于HTTP/3需要使用443的UDP协议端口,请注意开放对应的防火墙
  2. HTTP2监听的是443的TCP端口,而HTTP3监听的是UDP端口
  3. Nginx-Quic中添加了$http3和$quic变量,可以添加到日志中,这样就可以看到是否使用了HTTP/3来进行访问了
  4. 如果有多个server_name,在不指定IP的情况下,只需要在任意一个配置了 “listen 443 http3 quic reuseport;” 那么其他所有server_name都会开启HTTP3,其他域名不需要再添加该配置否则会报错,如果需要部分server_name开启HTTP/3,请指定监听IP。
  5. Nginx-Quic增加了http3_max_field_size、http3_max_table_capacity、http3_max_blocked_streams、http3_max_concurrent_pushes、 http3_push、http3_push_preload这六个变量来控制HTTP3的性能
  6. 如果要启用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支持HTTP/3(QUIC)折腾记》最先发表在NanNan's Blog。本文采用BY-CC-SA 4.0协议授权,禁止未经授权用于商业用途,如需非商业转载,请注明本文链接 https://blog.nannan.cool/archives/32/

评论

  1. ironbox
    Macintosh Chrome
    已编辑
    2年前
    2022-9-07 10:38:33

    最后编译nginx出现 ,我理解是版本问题嘛
    ./auto/configure: error: invalid option “–with-http_v3_module”

    • 博主
      ironbox
      Windows Chrome
      2年前
      2022-9-08 2:58:58

      请在这里下载 Nginx-Quic 分支的源码:hg.nginx.org/nginx-quic ,你可能使用的是主分支的。主分支还不支持 HTTP/3。

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇