总览
关于QUIC
QUIC(Quick UDP Internet Connections,快速UDP网络连接),是一个传输层协议,它基于UDP。相比于HTTP over TCP,HTTP over QUIC可以得到更好的连接质量,但会消耗更多的CPU资源。对于服务端来说,这意味着我们需要更多的服务器。
QUIC依然在开发中,目前有两个标准,分别是Google的gquic(gquic)和IETF QUIC(iquic),不过貌似在不久的将来就会合并。每个标准也有很多版本。这里可以看到所有的版本。
目前的QUIC有三个常见的实现:
- Chromium中QUIC的实现,在tools里还提供了一个样例server和client,此外还有一个epoll实现的server。
- quic-go是纯go写的QUIC。它在v0.10.0时还同时支持gquic和iquic,但由于两者差距开始变大,因此master分支支持iquic,gquic分支支持gquic。同样的,它也提供了一个样例server和client。
- Caddy(GitHub)是一个服务器软件,它本质上是把quic-go集成到了自己之中,因此与quic-go区别不大。目前的Caddy 0.11.1用的还是quic-go v0.10.0。
目标
- 搭建quic环境,尝试各种实现。
- 比较分析quic与tcp以及quic各种实现的服务端性能。
- 尝试优化性能。
环境
由于WSL的内存分配方面还有一些问题,无法正常编译Chromium,因此我们使用虚拟机:
- Ubuntu 18.04 LTS
- 3核CPU
- 8G内存
- 100G硬盘
安装和使用的所有软件如下:
- 2018-12-27下载的Chromium源码,此时最新的Chromium的release版本是71.0.3578.98。
- Caddy 0.11.1(with quic-go v0.10.0)。
- quic-go在2019-01-05 commit的版本。
- nginx 1.14.0
工具
做分析与测试需要各种工具,大致如下:
- taskset:查看和限定进程的CPU核的使用,例如限定一个进程只在某个核上跑。
- ifstat:查看网络带宽情况。
- perf/bcc:Linux下性能分析工具,可以分析出一个程序的热点。
- FlameGraph:将各种性能分析工具产生的数据画成火焰图。
- ProxyChains:可使任意程序走代理。
perf安装:sudo apt install linux-tools-common
。这里是一些例子,但perf有个缺点是很多符号无法解析,建议还是用bcc。
如果是升级了内核,可以通过Debian的包安装linux-perf-4.19(以后再也不用Ubuntu了):
wget http://security.debian.org/debian-security/pool/updates/main/p/perl/libperl5.24_5.24.1-3+deb9u5_amd64.deb
wget http://ftp.cn.debian.org/debian/pool/main/p/python3.5/libpython3.5_3.5.3-1+deb9u1_amd64.deb
wget http://ftp.cn.debian.org/debian/pool/main/p/python3.5/libpython3.5-stdlib_3.5.3-1+deb9u1_amd64.deb
wget http://ftp.cn.debian.org/debian/pool/main/p/python3.5/libpython3.5-minimal_3.5.3-1+deb9u1_amd64.deb
wget http://ftp.cn.debian.org/debian/pool/main/g/gdbm/libgdbm3_1.8.3-14_amd64.deb
wget http://security.debian.org/debian-security/pool/updates/main/p/perl/perl-modules-5.24_5.24.1-3+deb9u5_all.deb
sudo dpkg -i *.deb
wget http://ftp.cn.debian.org/debian/pool/main/l/linux/linux-perf-4.19_4.19.12-1~bpo9+1_amd64.deb
sudo dpkg -i linux-perf-4.19_4.19.12-1~bpo9+1_amd64.deb
bcc安装:sudo apt-get install bpfcc-tools linux-headers-$(uname -r)
自己编译的话(/opt/bcc-bin/share/bcc/tools/profile
):
sudo apt install llvm libclang-dev luajit libluajit-5.1-dev
git clone https://github.com/iovisor/bcc.git
cmake . -DCMAKE_INSTALL_PREFIX=/opt/bcc-bin
time make -j
sudo make install
popd
FlameGraph安装:git clone https://github.com/brendangregg/FlameGraph.git
后加入环境变量。
搭建环境
目录结构
工作目录在/opt
下。
.
├── caddy
├── caddy.conf #caddy配置文件
├── depot_tools
├── FlameGraph
├── nginx-fz.conf #nginx配置文件
├── quic-go
├── quic-test #工作目录(例如生成火焰图)
└── web_files #网页和测试数据目录
├── chromium-quic-data #chromium-quic的
│ └── www.example.org
│ ├── 1G.img
│ ├── index.html
└── html #quic-go和nginx的
├── 1G.img
└── index.html
其中,web_files只是文件存放的目录,在使用的时候需要复制到/dev/shm
以降低硬盘读写的影响。
一些配置
bashrc:
alias psef="ps -ef | grep "$LOGNAME
alias virc='vi ~/.bashrc'
alias sourc='. ~/.bashrc'
cd /opt
export PATH="$PATH:/opt/depot_tools"
export PATH="$PATH:/opt/FlameGraph"
Chromium下载与编译
主要可参考3个文档:
安装过程中需要挂代理,但是ProxyChains无效,不太清楚为什么。不过没关系,我们可以用命令行代理。我们需要把ss的允许局域网访问打开,在虚拟机中将代理设置为宿主机的IP,例如:
export http_proxy="http://192.168.64.1:1080"
export https_proxy="http://192.168.64.1:1080"
具体步骤如下:
安装depot_tools:
cd /opt
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
并加入环境变量(见上面bashrc)。接着下载Chromium:
mkdir chromium
cd chromium
fetch --nohooks chromium
这一步比较慢,我这里网速比较快,只用了3个小时。下面要安装依赖:
cd src
./build/install-build-deps.sh
中间可能会报错关于字体什么的,按它的提示加个参数忽略即可。如果是完整编译Chromium的话,可能要详细看一下官方文档来解决这个问题。
接下来是执行Chromium自己配置安装环境的工具,以及生成ninja的编译脚本:
gclient runhooks
gn gen out/Default
# --args='symbol_level = 2'
接下来就是编译QUIC了:
ninja -C out/Default quic_server quic_client epoll_quic_server
clean: ninja -C out/Default -t clean
Caddy安装
直接在这里下载release解压即可。
quic-go编译安装
这个我没有自己编译,是别人直接帮我编译好了个可执行程序,附带了自签名的证书(没什么用)。注意编译的时候要把example里的server和client编译出来。这个编译的时候可以设置让client忽略证书验证。
测试方法
准备工作
自签名证书
由于测试Chromium我们需要采用自签名证书,所以首先要生成并将它加入可信任的证书:
sudo apt-get install libnss3-tools
cd /opt/chromium/src/net/tools/quic/certs
./generate-certs.sh
cd -
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n quic -i /opt/chromium/src/net/tools/quic/certs/out/2048-sha256-root.pem
由于测试chromium用的www.example.org
域名,所以最好还是加个/etc/hosts
,这样便于浏览器访问。
生成测试文件
下面要生成QUIC下载的数据。
dd if=/dev/zero of=1G.img bs=1M count=1024
可以生成一个1G的全0文件。这个文件可以直接给Caddy和Nginx用,但是Chromium的server是要把http header写到文件开头才能使用的,为了方便,我们不用手动写header,可以利用wget来生成。首先开个http serversudo python -m SimpleHTTPServer 80
,然后wget -p --save-headers http://www.example.org/1G.img
,这样就是一个有header的1G文件了。同理,在加入hosts之前可以下个真的http://www.example.org/index.html
下来。
这些数据都在/opt/web_files
下面。我们每次在测试之前,要把它放入/dev/shm`以消除硬盘读写的影响:
cp -r /opt/web_files/* /dev/shm
浏览器扩展
想要在Chromium浏览器里查看QUIC的效果,需要安装HTTP/2 and SPDY indicator扩展程序。似乎命令行代理对Chromium没用,所以我们执行proxychains chromium-browser
装一下扩展,顺便把SwitchyOmega一起装了。
配置文件
Caddy:
localhost:80, localhost:443 {
root /dev/shm/html
errors stderr
log stdout
gzip
redir 301 {
if {scheme} not https
/ https://{host}{uri}
}
tls self_signed
}
Nginx:
server {
listen 8121;
root /dev/shm/html;
#listen 443 ssl http2;
server_name localhost;
ssl on;
ssl_certificate /opt/chromium/src/net/tools/quic/certs/out/leaf_cert.pem;
ssl_certificate_key /opt/chromium/src/net/tools/quic/certs/out/leaf_cert.key;
}
server与client的使用
Chromium server相关:
# Chromium quic_server
taskset -c 0 /opt/chromium/src/out/Default/quic_server \
--quic_response_cache_dir=/dev/shm/chromium-quic-data \
--certificate_file=/opt/chromium/src/net/tools/quic/certs/out/leaf_cert.pem \
--key_file=/opt/chromium/src/net/tools/quic/certs/out/leaf_cert.pkcs8
# Chromium epoll_quic_server
taskset -c 0 /opt/chromium/src/out/Default/epoll_quic_server \
--quic_response_cache_dir=/dev/shm/chromium-quic-data \
--certificate_file=/opt/chromium/src/net/tools/quic/certs/out/leaf_cert.pem \
--key_file=/opt/chromium/src/net/tools/quic/certs/out/leaf_cert.pkcs8
# Chromium浏览器(由于server没有开启tcp,所以必须强制浏览器开启QUIC)
chromium-browser --enable-quic --quic-host-whitelist="https://www.example.org/" --origin-to-force-quic-on=www.example.org:6121
# chromium-browser --enable-quic --quic-version=QUIC_VERSION_44 --quic-host-whitelist="https://www.example.org/" --origin-to-force-quic-on=www.example.org:6121
# Chromium quic_client
taskset -c 1 /opt/chromium/src/out/Default/quic_client --host=127.0.0.1 --port=6121 https://www.example.org/1G.img
taskset -c 2 /opt/chromium/src/out/Default/quic_client --host=127.0.0.1 --port=6121 https://www.example.org/1G.img
# quic-go client(其他参数:-num 2 -time 5)
taskset -c 1 /opt/quic-go/gquic_client -url https://www.example.org:6121/1G.img
taskset -c 2 /opt/quic-go/gquic_client -url https://www.example.org:6121/1G.img
quic-go server相关:
#必须它所在的目录下执行
cd /opt/quic-go
taskset -c 0 ./gquic_server -www /dev/shm/html -bind localhost:7121
# Chromium无法访问
chromium-browser --enable-quic --quic-version=QUIC_VERSION_44 --quic-host-whitelist="https://localhost/" --origin-to-force-quic-on=localhost:7121
# Chromium quic_client无法访问
/opt/chromium/src/out/Default/quic_client --host=127.0.0.1 --port=7121 https://localhost/1G.img
# quic-go client
taskset -c 1 /opt/quic-go/gquic_client -url https://localhost:7121/1G.img
Caddy相关:
sudo taskset -c 0 /opt/caddy/caddy -conf /opt/caddy.conf -quic
# Chromium无法访问
chromium-browser --enable-quic --quic-version=QUIC_VERSION_44 --quic-host-whitelist="https://localhost/" --origin-to-force-quic-on=localhost:443
# Chromium quic_client无法访问
/opt/chromium/src/out/Default/quic_client --host=127.0.0.1 --port=443 https://localhost/1G.img
# quic-go client
taskset -c 1 /opt/quic-go/gquic_client -url https://localhost/1G.img
quic-go(Caddy)开的server一律不能被Chromium quic_client访问,但Chromium quic_server却能被quic-go的client访问。怀疑虽然它们都是gquic,但在具体协议实现上还是有区别的。
TCP相关:
# quic-go TCP
taskset -c 0 ./gquic_server -www /dev/shm/html -bind localhost:7121 -tcp
taskset -c 1 /opt/quic-go/gquic_client -url https://localhost:7121/1G.img -tcp
# nginx
taskset -c 0 sudo nginx
taskset -c 1 wget --no-check-certificate https://localhost:8121/1G.img
性能查看
使用ifstat
可以查看网卡使用带宽-l
查看lo网卡。
ifstat -l
用bcc绘制火焰图,查看热点:
sudo profile-bpfcc -f -F 19999 -p `pidof quic_server` 2 > bcc-quic.res # 2s
sudo profile-bpfcc -f -F 19999 -p `pidof epoll_quic_server` 2 > bcc-quic.res # 2s
flamegraph.pl bcc-quic.res > bcc-quic.svg
perf也行,不过用了bcc之后就没用perf了:
sudo perf record --call-graph dwarf -s -F 19999 -p `pidof quic_server` sleep 2
sudo perf record --call-graph dwarf -s -F 19999 -p `pidof epoll_quic_server` sleep 2
sudo perf script -i perf.data | stackcollapse-perf.pl | flamegraph.pl > perf.svg
for 4.19内核:
sudo perf_4.19 record --call-graph dwarf -s -F 19999 -p `pidof quic_server` sleep 2
sudo perf_4.19 record --call-graph dwarf -s -F 19999 -p `pidof epoll_quic_server` sleep 2
sudo perf_4.19 script -i perf.data | stackcollapse-perf.pl | flamegraph.pl > perf.svg
此外,查看cache命中率:perf stat -e cache-misses ./exefilename
,具体到每个函数:perf record -e cache-misses ./exefilename
。
扩展与参考
- Caddy Web服务器QUIC部署
- 本站开始支持 QUIC
- 真有什么公司在测试环境中甚至生产环境中使用quic协议的吗?效果如何?国内运营商对udp的歧视到底有多严重?
- 《QUIC在⼿机微博中的应⽤实践》
- caddy quic 协议试用&& 几个问题
- QUIC Version 44 and IETF QUIC
- Caddy Web服务器QUIC部署
- QUIC Wire Layout Specification
- 试图取代 TCP 的 QUIC 协议到底是什么
- The QUIC Transport Protocol: Design and Internet-Scale Deployment
- 科普:QUIC协议原理分析
- 让互联网更快的协议,QUIC在腾讯的实践及性能优化
- TLS1.3/QUIC 是怎样做到 0-RTT 的
- QUIC传输格式规范(中文)
- QUIC Crypto Protocol(QUIC Crypto)
- QUIC - Developing and Deploying a TCP Replacement for the Web
Comments
注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。