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通信,没有增加服务中断风险。

QUIC简介-LMLPHP

Chrome调试

既然谷歌自家的网站已经启用了QUIC,我们可以访问试试看。怎么知道打开的网站是否走了QUIC协议呢,我们可以通过wireshark抓包验证,但还有 更方便的方法,直接用Chrome提供的工具,主要有三种方法:

  1. 开发者工具Security选项卡

    用Chrome浏览器打开谷歌官网www.google.com.hk ,按F12打开 开发者工具,切换到Security选项卡,刷新网页后选择左侧一个源站,可以看到 它使用的协议:

    QUIC简介-LMLPHP

  2. Chrome插件: HTTP/2 and SPDY indicator

    从Chrome应用商店搜索插件名字可以下载,它用不同颜色标识当前网站使用的是HTTP(灰色),HTTP2(蓝色)或者QUIC(绿色)

  3. 网络调试页 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连接。

开源实现

  • Chromium

    QUIC是谷歌的,chromium中的QUIC实现算是官方实现,保证QUIC版本最新,但整个项目代码量太大,网络模块依赖较多, 又使用了自己的一套编译工具,如果自己从中剥离QUIC协议栈也比较麻烦。

    chromium提供的C++接口很底层,需要自己实现会话、连接和流的管理,反向代理接收QUIC请求之后,需要构造UrlRequest发往后端。 要实现这些功能,还要先了解chromium的线程、消息循环、智能指针等一系列基础构件的使用方法,比较复杂,好在网上能找到一个样例。 这是一个尚在审核的提交 ,将来有望合入chromium主分支,能让用户快速启用QUIC尝鲜试用。

  • proto-quic

    从chromium剥离的一个QUIC协议部分,但是其github主页已宣布不再支持,仅作实验使用。

  • goquic

    goquic封装了libquic的go语言封装,而libquic也是从chromium剥离的,好几年不维护了,仅支持到QUIC 36, goquic提供一个反向代理,测试发现由于QUIC版本太低,最新chrome浏览器已无法支持。

  • quic-go

    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 周一

04-18 08:10