关于QUIC的一点研究
QUIC是什么
QUIC全称Quick UDP Internet Connections,是Google于2013年推出的一种新的网络传输协议,旨在解决传统http协议中存在的一系列问题。需要注意的是QUIC从一开始针对的应用场景就很明确,就是为了减少web latency。在Google的首发演讲中对QUIC的描述是“TCP + TLS + SPDY over UDP”(现在应该换成HTTP2了),所以QUIC很看重一些web比较关心的指标(比如0-RTT的连接建立,以及一条连接多路复用)。
QUIC要做什么
以TCP协议为例子,第一版的TCP于1981年在RFC-793中公布。距今已经20年有余,现在的网络状况早已经和当年大不一样。虽然TCP协议一直在更新,一直有着新特性在加入,但是有个矛盾的问题一直没有办法解决。那就是TCP做了太多的事情在传输层,并且都是在操作系统的内部实现的。这样做操作系统上的应用开发者就无需关心底层传输实现,可以直接调用系统的API来实现网络传输,否则开发一个应用就要开发一套传输算法,会造出来无数的轮子。但是同时也有一个问题,如果想要使用更新TCP协议版本,则需要操作系统的厂商来推进更新内核的TCP协议实现,而目前看操作系统的厂商的积极性很低。同时,仅仅你个人的操作系统更新了并没有用,还需要网络中大量的网络设备一起更新,这显然是不现实的。因此,只能把一些网络传输特性实现在更上层的应用中,尽管一个APP造一个轮子显然显得有些蠢,但是在浏览器内浏览网页却是个不错的应用场景。从目前看,操作系统内核的UDP协议实现 + 应用层自定义的可靠传输算法 + 实现一些更上面应用层的标准是这类轮子的标配。当然,也包括QUIC。
QUIC扮演的角色
1 | +++++++++++ ++++++++++++++ |
QUIC的优势
- Connection establishment latency:
相比于TCP+TLS建立连接需要3-RTT以上的时间,QUIC大部分情况下建立握手都可以做到0-RTT和1-RTT。 - Improved congestion control:目前Google版本的QUIC使用的拥塞控制算法其实就是把TCP的Cubic再实现了一遍,在一些边角如ack做了一些优化。
- Multiplexing without head-of-line blocking:在http2.0的一个特性是多路复用(Multiplexing),即某个客户端对同一个域名的多次请求只会使用同一条tcp连接。这样可以显著的减少建立tcp连接带来的开销,但是由于在tcp中是一个数据包丢失了会导致其之后的数据包都被卡住,而http2.0中使用又是同一条连接,这样就会导致“多路”同时卡住。QUIC则是可以同时支持多个独立的stream,stream之间的数据包在实际传输的过程中是穿插的,但是每个stream的数据却一定是严格FIFO的,这样某个包丢失了只会影响自己那一条stream。其实这点主要原因还是因为建立一条tcp连接的代价太高了,尤其在使用浏览器这种情景下人们本能的是想要复用同一条连接减少开销,只不过这时候tcp的“可靠性”却成了一种负担。
- Forward error correction:向前纠错能力,这点简单的来说就是比如你要发10个包,QUIC实际上给你发的是12个包,有2个包是由FEC算法根据前10个包的内容计算出来。这样这10个包中只要有2个以下的包丢了,接受方根据同样的FEC算法可以还原出那几个丢失的包。这本质上是一种冗余策略,虽然会显著的提高数据量但是在已知网络情况较差的时候会显著降低延迟(重传带来的延迟更大)。类似的算法在KCP和很多其余的网络库中也可以看到。
- Connection migration:TCP的协议中标记一条连接的唯一标识是一个来源IP地址+来源端口+目标IP地址+目标端口的4元组构成的。如果移动设备一旦切换了基站或者4G切到了Wi-Fi又或者某个NAT路由器的映射表动态刷新了等行为都会直接导致TCP断线。QUIC使用的是一个64位的唯一ID来标识的,这样即使切换了网络,应用层也是无感的。不过QUIC中这个64位的唯一ID是客户端自己生成的,这里可能有人会问为什么不是服务器来生成?答案其实就在上面,是因为QUIC要做到0-RTT的握手,如果是服务器生成,那么建立连接的步骤就会多一步服务器发送生成好的唯一ID给客户端并且等待客户端的确认回包。但是这里有一个无法避免的问题,那就是connection id collision。不过好在有人算过两个64位的随机数作id,在一个100,000并发的服务器上碰撞的概率大概是3*10^-10。考虑到QUIC应用场景,似乎可以理解为什么会这样做。
QUIC的一些问题
目前QUIC协议在IETF工作组中还处于频繁修改的状态,IETF工作组也给出了一些协议实现建议,github上也有人在造一些轮子(比如这个golang版本的实现 https://github.com/lucas-clemente/quic-go )。在众多选择和没有统一标准的面前的是数不清的坑(是使用Google QUIC的实现还是IETF QUIC第三方实现?)真正能够做出稳定上线产品的还是凤毛麟角(这里有一篇关于微博的应用笔记 http://www.infoq.com/cn/news/2018/03/weibo-quic )。同时,在健康的网络状况下QUIC和TCP相比并没有优势,在弱网情况下是有的。如果坚持使用QUIC作为线上产品的网络传输解决方案的话,可以备用一套TCP的方案,在测试到一些网络封闭了UDP包这种情况时可以随时回滚。
最后,期待2019年能形成QUIC的RFC技术规范。
关于QUIC的一点研究
install_url
to use ShareThis. Please set it in _config.yml
.