01月16, 2019

关于QUIC的各种尝试

总览

关于QUIC

QUIC(Quick UDP Internet Connections,快速UDP网络连接),是一个传输层协议,它基于UDP。相比于HTTP over TCP,HTTP over QUIC可以得到更好的连接质量,但会消耗更多的CPU资源。对于服务端来说,这意味着我们需要更多的服务器。

QUIC依然在开发中,目前有两个标准,分别是Google的gquic(gquic)和IETF QUIC(iquic),不过貌似在不久的将来就会合并。每个标准也有很多版本。这里可以看到所有的版本。

目前的QUIC有三个常见的实现:

目标

  • 搭建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

扩展与参考

UDP GSO

本文链接:https://debug.fanzheng.org/post/about-quic.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。