QUIC简介
QUIC是Quick UDP Internet Connections的缩写,谷歌出品的传输协议,使用UDP替换TCP来支持HTTP通信。UDP本身不是可靠传输, 通过在其上添加流控、校验等功能,QUIC可以实现可靠传输,而且相比于TCP,它的流控功能在用户空间而不在内核空间,那么使用者就 不受限于CUBIC或是BBR,而是可以自由选择,甚至根据应用场景自由调整优化。
QUIC为现有 tcp + https + http2
组合存在的诸多问题提供了解决方案:
- 使用缓存,减小握手延迟
- 改善拥塞控制,拥塞控制从内核空间到用户空间
- 多路复用中避免head-of-line堵塞,多路流互不影响
- 前向纠错,减少重传
- 连接平滑迁移,如手机从4G切到Wifi,应用不掉线
QUIC应用
最新版的Chrome浏览器默认启用QUIC协议,只要后端服务器支持,Chrome就会使用QUIC协议。现在谷歌自家的大部分网站都使用了QUIC协议。
不改变现有Web服务架构,我们可以通过在内网添加一层反向代理,快速启用QUIC协议。这样,对于支持QUIC的客户端,使用QUIC协议改善外网通信, 对于不支持QUIC的客户端,依旧沿用TCP通信,而且就算这个反向代理挂掉了,Chrome浏览器还可以切回TCP通信,没有增加服务中断风险。
Chrome调试
既然谷歌自家的网站已经启用了QUIC,我们可以访问试试看。怎么知道打开的网站是否走了QUIC协议呢,我们可以通过wireshark抓包验证,但还有 更方便的方法,直接用Chrome提供的工具,主要有三种方法:
-
开发者工具Security选项卡
用Chrome浏览器打开谷歌官网www.google.com.hk ,按F12打开 开发者工具,切换到Security选项卡,刷新网页后选择左侧一个源站,可以看到 它使用的协议:
-
Chrome插件:
HTTP/2 and SPDY indicator
从Chrome应用商店搜索插件名字可以下载,它用不同颜色标识当前网站使用的是HTTP(灰色),HTTP2(蓝色)或者QUIC(绿色)
-
网络调试页
chrome://net-internals
Chrome还提供一个url用于调试网络状态,在浏览器中访问
chrome://net-internals/
地址,或者点击上述插件的小图标即可进入。 这里提供了非常丰富的监控和调试功能,我们重点需要关注QUIC页面和Alt-Svc页面。点击左栏的QUIC可以查看浏览器QUIC的版本配置以及QUIC会话。
点击左栏的Alt-Svc可以查看站点QUIC的端口、超时等情况。本页面的一个示例如下表:
| --------------Server -------------------| Alternative Service -------------| | ------------------------------------------ | ---------------------------------------- | | `https://client4.google.com` | quic :443, expires 2018-04-18 16:54:41 | | `https://zabbix.test.com` | quic :443, expires 2018-03-20 16:53:30 (broken) |
可以看到client4.google.com这个域名启用了QUIC协议,在UDP 443端口,后面是缓存超时。特别需要注意的是,zabbix.test.com这个地址, 它的最后有个(broken),意思是说虽然这个网站宣称自己支持QUIC协议,即有响应头
Alt-Svc: quic:443
,但是浏览器发出的QUIC请求 未得到响应。这种情况有可能是防火墙屏蔽了UDP 443端口,也可能是后端的QUIC服务挂了。Chrome浏览器初次发出QUIC请求会使用的赛跑的方式,同时发出TCP和QUIC的请求,那个先返回就用哪个,确认QUIC服务可用之后,后续请求 直接使用QUIC协议。但是,如果发现QUIC通道不可用了,它就把它标记为(broken),切回TCP连接。
开源实现
-
QUIC是谷歌的,chromium中的QUIC实现算是官方实现,保证QUIC版本最新,但整个项目代码量太大,网络模块依赖较多, 又使用了自己的一套编译工具,如果自己从中剥离QUIC协议栈也比较麻烦。
chromium提供的C++接口很底层,需要自己实现会话、连接和流的管理,反向代理接收QUIC请求之后,需要构造UrlRequest发往后端。 要实现这些功能,还要先了解chromium的线程、消息循环、智能指针等一系列基础构件的使用方法,比较复杂,好在网上能找到一个样例。 这是一个尚在审核的提交 ,将来有望合入chromium主分支,能让用户快速启用QUIC尝鲜试用。
-
从chromium剥离的一个QUIC协议部分,但是其github主页已宣布不再支持,仅作实验使用。
-
goquic封装了libquic的go语言封装,而libquic也是从chromium剥离的,好几年不维护了,仅支持到QUIC 36, goquic提供一个反向代理,测试发现由于QUIC版本太低,最新chrome浏览器已无法支持。
-
quic-go是完全用go写的QUIC协议栈,开发很活跃,已在 Caddy 中使用,MIT许可,目前看是比较好的方案。它的接口和goquic接口 很类似,可以参考goquic反向代理的 代码 ,把QUIC部分替换为quic-go即可。最简单的实现方法,初始化一个反向代理handler 直接调用h2quic.ListenAndServeQUIC,主要代码如下:
import ( "net/http/httputil" "github.com/lucas-clemente/quic-go/h2quic" ) proxyHandler := httputil.NewSingleHostReverseProxy("localhost:80") err = h2quic.ListenAndServeQUIC(addrStr, cert, key, proxyHandler)
编译部署
编译
要编译这个反向代理,需要把整个chromium项目下载下来,可以按照 教程 操作,然后checkout这个 包含代理的提交 ,用ninja编译, 编译时只选择 quic_server
和 quic_client
即可。这个反向代理的实现,在 quic_server
的基础上加了一个启动选项 --mode=proxy
来开启反向代理模式,比如原来的服务在本机的80端口,使用原来的证书和私钥启动反向代理:
./quic_server --mode=proxy --quic_proxy_backend_url=http://localhost --certificate_file=leaf_cert.pem --key_file=leaf_cert.pkcs8
启动之后可以使用QUIC测试客户端连接试试看:
./quic_client --host=127.0.0.1 --port=6121 https://www.example.org/index.html --disable-certificate-verification
nginx配置
第一次http请求还是会从TCP端口先到达原始服务器,通过在响应中添加Alt-Svc头告诉客户端能够支持QUIC, 以nginx为例,在配置文件的http块中添加:
add_header Alt-Svc 'quic=":443"'
这时候用Chrome浏览器去访问你的网站,就可以使用QUIC协议了。
根据上面介绍的方法,去Chrome浏览器中验证一下,如果指示器不是绿色的,可以点开看看是否(broken)了,尝试抓包查找网络原因,调整防火墙策略。
移动端
移动互联网时代,光依靠Chrome浏览器可不行啊,谷歌提供了 cronet ,它是chromium网络协议栈的封装,可以用于Android和iOS平台,很方便 集成到手机APP中。 这里 可以下载编译好的cronet库。
2018-03-19 周一