lwn原文https://lwn.net/Articles/745590/

TCP协议无处不在,以至于人们几乎把“TCP/IP”和“网络”画上了等号。引入新的协议(甚至对当前协议的修改)几乎无法实现。Linux.conf.au 2018上,来自Google的开发者Jana Iyengar却向大家介绍QUIC protocol已经在Internet上占据了7%左右的流量。

QUIC(quick UDP Internet connection)是专门为HTTP传输层设计的,经过了多年开发之后在2014年Google开始部署。它主要用于Chrome浏览器或是其他APP与Google服务器之间传输数据,应用之后YouTube上的重复缓冲下降了15-18%,Google search的延迟也下降了3.6-8%。考虑到这些应用场景已经进行过非常多的优化,QUIC的效果实在让人难以置信。

QUIC的使用在2015年的时候增长很慢,同年的12月QUIC更是遇到了一个非常严重bug,一些请求被以未加密的形式发送出来,从而导致了非常严重的安全隐患。于是所有应用被迫全部停止使用QUIC这个功能直到bug被最终fix。2016年8月,YouTube APP上也开始使用QUIC,于是乎QUIC的使用率相比15年12月停用之前整整翻了一番。如果有人怀疑计算的未来在移动端,这个数据可以作为足够的证据了。更可怕的是,目前Google的35%发往外部的流量都在使用QUIC。

万维网使用的标准网络栈,自上到下包括HTTP层、TLS密码层和TCP层。与之对应的,QUIC使用了一套完整的协议栈来替换,首先QUIC基于UDP协议实现,在这个基础上实现了可靠的面向连接的协议,以及类似TCP的拥塞控制。同时QUIC也有对加密和HTTP的支持,可以把加密通信和握手放在一个数据包里面。截止到目前,QUIC的开发和部署主要都是Google在做。不过2016年一个致力于将QUIC标准化的IETF工作组成立了,工作组目前的工作之一是把当下的QUIC加密层用基于TLS 1.3的一个新实现来替代。

HTTP加速

通常的网页有很多需要从服务器加载的对象(HTML、CSS以及图片等),而HTTP/1.x 协议仅仅允许这些对象依次传输,所以容易造成排头阻塞(head-of-line blocking),从而影响页面的加载时间。使用HTTP/1.x 的程序往往建立多个链接同时获取目标,但是这样处理有一些已知问题,比如多链接占用了较多的资源,同时他们之间互相竞争也不能由统一的流控算法来处理。

HTTP/2使用了单个链接内的多个“流”来发送数据,多个对象可以走多个“流”同时发送到对端。这样做有一些效果,但仍然不够好:比如某个“流”上的丢包会把其他“流”上面的流量卡住,因为这些流实际上用的是一个TCP链接,于是引入了新的head-of-line blocking,所以貌似尝试在HTTP层解决TCP引入的问题不切实际。

TCP同时还有一些其他问题,比如链接建立的延迟高,而这对web服务的影响非常大。另外,大量路由器的存在,使得要改善TCP协议非常困难。路由器本不应该看TCP头,但实际它们却经常这么做,并且根据里面的信息来阻塞某些流量。于是我们看到TCP fast open已经进入Linux kernel多年,却并未全面部署起来。

QUIC可以很好的解决上面这些问题,两台机器通过QUIC通信的时候,实际只有第一次建立QUIC链接的时候才需要一次网络包的来回乒乓。后续其他链接建立的时候可以直接利用已经建立链接的信息。HTTP stream也被直接映射到QUIC stream上,一个丢包不会影响其他流。这些改进加起来就降低了广域网环境多个因素造成的延迟。

Requirements,metrics,implementations

QUIC开发者需要设计一个容易部署和迭代的协议,于是很自然想到了UDP,因为路由器并不会太多的干扰UDP包。用户态程序的开发也很方便,建立连接延迟低、多stream支持也是必选项。除了这些,拥塞控制的算法选择也变得更加灵活。

另一个问题是对“NAT rebinding” 的处理。当前很多互联网上的数据请求都通过了一层NAT网关的转换,而由于NAT可以看到TCP的SYN和FIN请求,所以NAT网关是可以很方便的确定NAT映射的生命周期。而UDP则不一样,由于UDP并没有“链接”这个概念,所以NAT网关并没有一个好的方法来确认NAT映射的生命周期,所以只能依靠timer来管理UDP端口映射的生命周期。这样的结果就是一旦定时器到期之后如果这个链接实际上还是活跃的,那么NAT网关会为此创建一个新的NAT映射并使用了新的端口,于是QUIC流量看起来就像是突然从别的端口来了,所以QUIC必须有办法来处理这种情况。

这里也许有人好奇,为什么在UDP之上而不是直接在IP之上实现QUIC?Iyengar用SCTP协议举例说,由于中间的网络设备更新很慢,所以新的基于IP协议仍然可能由于中间的网络设备无法识别而被丢弃。比如SCTP已经诞生很长时间,目前仍然有网络设备不认识它。。。

上文提到QUIC显著提升了Google服务质量,这部分延迟的降低主要来自建立链接阶段。在网络比较慢的环境里面加速的效果更好:在韩国发起的Google search延迟仅下降了1.3%而印度发起的search延迟则降低了13%。Iyengar还说用上QUIC之后用户更愿意看视频了。。。

QUIC的另一个特性是UDP数据包中的传输层头部被加密了,除了显而易见的保密性提升之外,加密还可以避免由于网络设备协议老化带来的影响,这样的网络设备不可能根据看不懂的信息作出路由决定。目前QUIC只有极少数的信息需要明文传输,其中一项是链接ID。谈到这里,Iyengar举了一个例子来说明网络设备协议老化的影响。明文数据开始的地方是一个标志位,这个地方很不幸的被少数网络厂商错误的进行了处理,导致一个新的标志被置位之后数据包被丢弃。这再一次说明网络协议的进步受到了网络设备协议老化的影响,想要避免只能进行加密。

与会者还讨论了其他一些实现方面的问题,不过大多还没落地。go语言的实现版本quic-go的研发也在进行中。另外Apple和Microsoft也在做QUIC的实现,不过显然这些实现版本之间的互操作性还有大量的测试工作。至于QUIC的开源版本,Iyengar提到了chromium 浏览器中的实现可以供大家参考。最后的最后,就是期待一下IETF标准化了。

视频资料请戳这里