QUIC探索(一):初识QUIC
前言
此系列会以一系列文章来介绍Google在传输层的新开源贡献:QUIC协议
的一些内容,也是传说中未来的HTTP3。
当然,系列开篇肯定要先介绍QUIC是什么,能做什么,为什么要用QUIC这些哲学问题,所以这篇文章不会涉及一些代码层面的东西。
QUIC是什么
QUIC(全称Quick UDP Internet Connections)是谷歌公司制定的一种基于 UDP 协议的低时延互联网传输协议,它提供了多项改进,旨在加速HTTP传输并使其更加安全,目标是想最终取代TCP和TLS协议。
可以用一个公式大致概括如下:
TCP + TLS + HTTP2 = UDP + QUIC + HTTP2’s API。
【注意以上都是去别人家摘录的】
请注意,QUIC是由Google最早提出并在chromium实现,而现今交给IETF推进标准化工作。所以当前QUIC有两大分支流派,一种是基于Google的Chromium工程的QUIC实现,也叫做gQUIC;另外一种是标准化进行中的版本也叫做iQUIC。
目前两者由于协议格式、加密方式等等的不同而不能互通。当然从现时QUIC协议开源库支持度、稳定性和多平台兼容来看,比较建议采用Google主导的gQUIC,并且gQUIC在后续也会打通与iQUIC的兼容。
还有一点就是对于QUIC版本的话,在Google的源码更新进度上面看其版本迭代非常迅速,所以这里强烈不建议一味求新,除非你是大神或者想当小白鼠除外,因为每一个版本的升级导致新版本的协议格式和API变动较大而忙于处理变更。这里建议从一个稳定的版本进行入手研究,例如业界广泛支持的QUIC43/44。
QUIC的优势
低延迟连接的建立
对于传统的HTTPS来说,对于其传输层的TCP握手就需要3个RTT,如果算上加密部分的话还需要产生额外的RTT,也就是说HTTPS进行一次完全的握手至少需要4个以上的RTT。
然而对于QUIC来说,如果是客户端首次连接到服务器,由于QUIC将传输与加密结合在一起的特性所在,一般来说正常情况下初次握手只需要1个RTT就可以完成握手;但是对于触发版本协商、证书无法解密等问题当然也会导致多个RTT的产生。
而重复连接的情况下握手,如果在证书有效的情况下,客户端发送Hello包并不用等待回复就可以直接发数据加密包,也就是实现了传说中的0RTT。
改进的拥塞控制
TCP 的拥塞控制实际上包含了四个算法:慢启动,拥塞避免,快速重传,快速恢复。
QUIC协议当前默认使用TCP的拥塞控制算法,并在其基础上进行了相应的改进;当然QUIC也支持其他的拥塞控制算法。
主要的改进点有:
1、可插拔设计
2、单调递增的Packet Number
3、不允许Reneging
4、更多的Ack块
5、精确计算RTT时间
无队头阻塞的多路复用
HTTP2的最大特性就是多路复用,而HTTP2最大的问题就是队头阻塞。
首先了解下为什么会出现队头阻塞。比如HTTP2在一个TCP连接上同时发送3个Stream,其中第2个Stream丢了一个Packet,TCP为了保证数据可靠性,需要发送端重传丢失的数据包,虽然这时候第3个数据包已经到达接收端,但被阻塞了,这就是所谓的队头阻塞。
而QUIC多路复用可以避免这个问题,因为QUIC的丢包、流控都是基于Stream的,所有Stream是相互独立的,一条Stream上的丢包,不会影响其他Stream的数据传输。
前向纠错
QUIC协议的每个数据包除了本身的数据以外,会带有其他数据包的部分数据,在少量丢包的情况下,可以使用其他数据包的冗余数据完成数据组装而无需重传,从而提高数据的传输速度。具体实现类似于RAID5,将N个包的校验和(异或)建立一个单独的数据包发送,这样如果在这N个包中丢了一个包可以直接恢复出来,除此之外还可以用来校验包的正确性。
连接迁移
对于TCP协议来说,标识一个TCP连接需要4个参数,既来源IP、来源端口、目的IP和目的端口。其中的任一参数改变,TCP连接就需要重新创建。这对于传统网络来说影响不大,因为来源和目的IP相对固定。但是在无线网络中,情况就大不相同了。设备在移动过程中,可能会因为网络切换(如从WIFI网络切换到4G网络环境),导致TCP连接需要重新创建。
QUIC协议使用了UDP协议,不再需要这四元组参数。同时QUIC协议实现了自己的会话标记方式,称为连接UUID。当设备网络环境切换时,连接UUID不会发生变化,因此无需重新进行握手。
PS:基于QUIC相对于TCP的优点的网上文章已经烂大街了,其主要是出自Google的文档翻译。这里简单的进行了一些的介绍就过了,如果还想进一步了解的话建议参考其他文章或者Google发布的QUIC相关的文档,但是对于开发实战意义并非是非常重要的,只要还是源码为王,暂时觉得只是合适吹水 O(∩_∩)O
QUIC相关开源库
chromium:quic-client/server-demo模块
参考地址:https://www.chromium.org/quic
Google提供的一个QUIC的源码使用Demo,但是值得注意的其是封装了支持HTTPS的QUIC实现,如果你想在模仿TCP Socket进行QUIC传输开发的话这个方案可能不适合你。
重点敲黑板,这个Demo主要用于集成测试,其并不具备大规模的生产环境使用的性能的可能性;换句话说,它就只是一个玩具,但是这个玩具挺值得你去玩味。
PS:后续会有一篇该部分的源码简单剖析的文章。
chromium:net模块
参考地址:https://chromium.googlesource.com/chromium/src.git
如果你想在Android、iOS、Linux上面更灵活的使用QUIC的话,我觉得chromium的net模块是你的最好选择。
如果你需要封装QUIC在HTTP/HTTPS上面使用的话,可以参考上面的 quic-client/server-demo
的源码的相关使用方法,其实也就主要是研究QUIC源码库在spdy部分的内容。
如果你需要封装QUIC在更底层模仿TCP Socket操作的话,不妨看看quartc这个模块下面的API实现,具体的参考net下面的 quartc_session_test.cc
这个文件或者参考github上面的开源库posix_quic,不过后者是基于libquic的,API的调用流程并不一定适合你开发的版本,但是可以提供大方向的参考。
PS:后续的文章也大部分是基于这个模块展开。
quic-go
参考地址:https://github.com/lucas-clemente/quic-go
quic-go是使用Go语言来重写的QUIC协议实现库,从github上面看其对于iQUIC和gQUIC这两个分支流派都提供了支持,这个库当前也是比较活跃的。
从测试结果来看其稳定性和对于多端的支持相对于chromium来说仿佛就是一个小弟弟,但其也不能掩盖这个就是以前我们爸妈口中别人家的好孩子般存在。当然其性能还需要打磨,对于大规模线上应用还是需要谨慎考虑。
libquic
参考地址:https://github.com/devsisters/libquic
libquic已经有多年没有更新了,其应该是民间从chromium中提取QUIC相关源码以及其依赖项而形成一个简易的开源代码库,对于苦于需要找个梯子下载动辄数十个GB的chromium源码开发者来说,无疑是一个福音,这个库很方便我们快速尝试QUIC的开发。
其支持 ninja 和 cmake 两种编译方式,但是遗憾的是从反馈上来看这个库并不支持iOS平台的编译。
基于这个平台的 HTTP 封装实现有 goquic 和 TCP Socket 封装实现有 posix_quic。
proto-quic
参考地址:https://github.com/google/proto-quic
这个库是Google在chromium上面抽取出来发布于github的一个快速验证QUIC的开源库,同libquic
一样并不需要下载太多的源码,但是其仅仅保证在Ubuntu上是可用的,现在Google已经转向了quiche
这个开源分支上面进行独立QUIC库的开发。
在笔者写这篇文章的时候,其github上面的代码已经被清空;不难预期这个源码被整个删库跑路的日子也不会太远了。
quiche
参考地址:https://quiche.googlesource.com/quiche
更新速度极快的谷歌QUIC开源代码库,其主要目的是希望将QUIC从chromium这个庞大的库独立出来作为其上游的实现方案来提供QUIC协议的支持,但是由于其也只是刚刚开始从 https://cs.chromium.org/chromium/src/net/third_party
中迁移过来,目前相关文档比较缺乏而且源码结构变动过大;暂时并不建议入手研究,但是强烈建议重点关注。
chromium:Cronet模块
参考地址:https://developer.android.com/guide/topics/connectivity/cronet
Cronet主要为chromium的net模块进行了Android/iOS端的封装,并提供了相应的Java和OC接口,所以我们在移动端也是可以通过Cronet使用net模块里面QUIC协议。如果在客户端APP想要快速验证使用基于QUIC的HTTP请求的话,Cronet是一个非常合适的方案,但是在简单使用的前提下其灵活性也相对较差。
Stellite
参考地址:https://github.com/line/stellite
这个是利用了Cronet,用C++封装了一层API而得到的这个Stellite开源库,解决了我们希望能在C/C++层面进行简单快速使用QUIC相关协议的需求。当然,对于灵活性和效率肯定没有比不上chromium的原生逻辑实现。
Caddy
参考地址:https://github.com/mholt/caddy
Caddy是当前支持QUIC的一个比较健壮的Web服务器,其底层是基于quic-go的实现。相对于nginx等框架还未提供对quic的支持,实验性支持quic的caddy也是当前web服务器支持QUIC协议的唯一理想选择。
结语
好了,这篇文章主要目的就是简单介绍一下QUIC这个协议,以及可用于研究QUIC的一些开源库,接下来主要会围绕chromium库来介绍一下基于QUIC的相关开发工作。
本文发布于 简书。
End!