在越来越讲究用户体验的今天,网络带宽的提高已经很难有显著的页面加载改善,而低延迟的优化往往能够起到意想不到的效果。在《TLS1.3/QUIC 是怎样做到 0-RTT 的》中我们分析了TLS1.3和QUIC在低延迟方面的原理和低延迟优势。在从源代码编译 nginx docker 镜像开启 TLS 1.3中我们已经把玩了TLS1.3,没有理由不把玩一下QUIC,对吧?
起初以为,在普及程度上,QUIC因为主要是Google主导,会曲高和寡。但是,查了一下,发现腾讯早在2017年就在生产环境应用了QUIC:让互联网更快的协议,QUIC在腾讯的实践及性能优化. 结果显示:
灰度实验的效果也非常明显,其中 quic 请求的首字节时间 (rspStart) 比 http2 平均减少 326ms, 性能提升约 25%; 这主要得益于 quic 的 0RTT 和 1RTT 握手时间,能够更早的发出请求。
此外 quic 请求发出的时间 (reqStart) 比 h2 平均减少 250ms; 另外 quic 请求页面加载完成的时间 (loadEnd) 平均减少 2s,由于整体页面比较复杂, 很多其它的资源加载阻塞,导致整体加载完成的时间比较长约 9s,性能提升比例约 22%。
既然大厂都已经发车,我司也就可以考虑跟进了。稳妥起见,决定先在自己的博客开启QUIC,然后再逐步在线上业务进行推广。
方案概览
方案非常简单:不支持QUIC的浏览器依旧通过nginx tcp 443访问;支持QUIC的浏览器通过caddy udp 443访问。
由于nginx近期没有支持QUIC的计划, 作为一名gopher, 因此这里选择caddy作为QUIC的反向代理。后面会介绍caddy的具体安装和配置方法。
对于支持QUIC的浏览器来说,第一次访问支持QUIC的网站时,会有一次服务发现
的过程。服务发现的流程在QUIC Discovery
有详细介绍。概括来说,主要有以下几步:
- 通过TLS/TCP访问网站,浏览器检查网站返回的http header中是否包含
alt-svc
字段。 - 如果响应中含有头部:
alt-svc: 'quic=":443"; ma=2592000; v="39"'
,则表明该网站的UDP 443端口支持QUIC协议,且支持的版本号是draft v39; max-age为2592000秒。 - 然后,浏览器会发起QUIC连接,在该连接建立前,http 请求依然通过TLS/TCP发送,一旦QUIC连接建立完成,后续请求则通过QUIC发送。
- 当QUIC连接不可用时,浏览器会采取5min, 10min的间隔检查QUIC连接是否可以恢复。如果无法恢复,则自动回落到TLS/TCP。
这里有一个比较坑的地方:对于同一个域名,TLS/TCP和QUIC必须使用相同的端口号才能成功开启QUIC。没有什么特殊的原因,提案里面就是这么写的。具体的讨论可以参见Why MUST a server use the same port for HTTP/QUIC?
从上面QUIC的发现过程可以看出,要在网站开启QUIC,主要涉及两个动作:
- 配置nginx, 添加
alt-svc
头部。 - 安装和配置QUIC反向代理服务。
配置nginx, 添加alt-svc
头部
一行指令搞定:
1 2 |
add_header alt-svc 'quic=":443"; ma=2592000; v="39"'; |
安装QUIC反向代理服务器caddy
上面我们提到对于同一个域名,TLS/TCP和QUIC必须使用相同的端口号才能成功开启QUIC。然而,caddy服务器的QUIC特性无法单独开启,必须与TLS一起开启,悲剧的是TLS想要使用的TCP 443端口已经被nginx占用了?
场面虽然有点尴尬,但是我们有docker:将caddy安装到docker中,然后只把本地的UDP 443端口映射到容器中即可。
于是我们创建了一个docker-caddy项目。Dockerfile 10行内搞定:
1 2 3 4 5 6 7 8 9 10 |
FROM ubuntu:16.04 LABEL maintainer="liudanking@gmail.com" RUN apt-get update RUN set -x \ && apt-get install curl -y \ && curl https://getcaddy.com | bash -s personal && which caddy |
caddy 服务配置文件/conf/blog.conf
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
https://YOUR_DOMAIN tls YOUR_CERT_KEY_DIR/chained.pem YOUR_CERT_KEY_DIR/domain.key proxy / http://VPS_PRIVATE_IP:81/ { header_upstream Host YOUR_DOMAIN header_upstream X-Real-IP {remote} header_upstream X-Forwarded-For {remote} header_upstream X-Forwarded-Proto {scheme} } log /conf/blog.log |
启动docker:
1 2 |
docker run -d --name caddy-blog -p 443:443/udp -v YOUR_CERT_KEY_DIR: YOUR_CERT_KEY_DIR -v /conf:/conf liudanking/docker-caddy caddy -quic -conf /conf/blog.conf |
开启Chrome浏览器QUIC特性
在chrome://flags/中找到Experimental QUIC protocol
, 设置为Enabled
. 重启浏览器生效。
测试QUIC开启状态
重新访问本站https://liudanking.com, 然后在浏览器中打开:chrome://net-internals/#quic。如果你看到了QUIC sessins,则开启成功:
当然,你也可以给Chrome安装一个HTTP/2 and SPDY indicator(An indicator button for HTTP/2, SPDY and QUIC support by each website) 更加直观的观察网站对http/2, QUIC的支持情况。