WWDC22 - iOS网络延迟优化L4S技术

2,234 阅读17分钟

我正在参加「掘金·启航计划」

前言

网络延迟,是我们在开发中经验会提到及谈论的一个术语。网络延迟影响着以网络交互为核心的应用在各个场景下的体验,所以,开发者追求网络低延迟,就成了一个永恒的课题。如何让音视频播放更流畅、如果远程会议的同步性更好、又或者更好的页面加载时间以及更短的loading时间。

2022,Apple又给我们带来了一篇关于改善应用网络延迟的Session!

2021,Apple在 Reduce network delays for your app 这边Seesion中介绍了一些关于网络延迟相关优化的知识。

今年的这篇Seesion从App到服务端,再到网络协议都提出了一些不错的改善建议,能够帮我开发者更好的分析评估自己引用的网络延迟状况,并以此能进行更多的优化改进。

Session 10078 - Reduce networking delays for a more responsive app

强力建议观看Session!!!

网络延迟现象

了解网络延迟

相信大家都会玩游戏,无论PC游戏,还是手机游戏,很多对抗类的游戏都会在游戏界面实时演示网络延迟的量值,来提醒玩家当前的网络环境

一般来说1~30ms 网络极佳,你出🔫可能比别的玩家快

30~50ms,一切正常。

50~100ms,还能坚持游戏,此时已经可以感受到场景的流畅性降低了,尤其在现在高刷新率的手机上。

100ms以上,基本就是劝退了。

这就是网络延迟,数据包从A端到B端所消耗的时间,它决定了数据内容在服务端产生后,多久能到达你的应用(设备)上。所以延迟越高,体验则越差。

尝试降低网络延迟

我们可以先看一张Session中的图

图中的三个窗口,其实已经描述了一些常见的降低网络延迟的手段,虽然这里想表达的是这些手段并没有起到作用,这是后面要说的。这里先看图中的内容:

  1. 提升带宽

  2. 更换网络设备(路由器)

  3. 安装无线网格mesh系统

作为普通用户,在遇到网络延迟影响使用或者想提升体验的时候,能使用的办法其实就是这些。但是作为开发者,我们知道这些措施本质上只是在提升带宽和优化网络质量,没有办法完全避免。

探究网络延迟

我们先来看看App的数据包是怎么在网络中传输的。来看一张ession中的图

如图,一个服务,通常有多个服务节点,每个节点的网络延迟都不一样。其中最慢的那个节点,通常就会积压大量的待处理数据包,形成一个等待队列(图中黄色方框),所以你从App发起的请求必须在队列积压任务处理完之后才能到达服务端,这样就增大往返时间。带宽的增加对这种情况就起不到改善作用。所以就出现了上图的对话框内容。

假设,我们不考虑变更任何网络环境:如带宽,网络硬件设备质量,能否从网络协议角度去优化网络延迟的问题呢?我们来看看一个计算机网络中重要的性能指标RTT时间。

RTT时间

RTT(Round Trip Time)往返时间,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。RTT是计算机网络中的一个重要性能指标,RTT时间大小由三个因素决定:

  • 链路的传播时间

  • 末端系统的处理时间

  • 路由器排队和处理时间

前两个在TCP链接中比较固定,所以,路由器的排队时间和处理时间会因为积压的变化而变化,所以RTT的变化在一定程度上反映了网络拥塞程度。我们先来看,一个正常的HTTPS的请求,直接看Session的图

假如你网络基础知识薄弱,不太理解Session中的的这张图。那我们延伸探究一下,这里给大家补充了一张图:

UML 图.jpg

大家可以用wireshark抓包验证一下是不是如图所示这样。

现在我们知道了硬件处理的方式,也知道了网络传输过程中的关键性能指标RTT。

所以,在理想硬件环境下,需要优化网络延迟时间,我们就需要从缩短RTT和减少RTT次数来入手,苹果在WWDC2021 Session中也提出了RPM概念及相关的测试工具,意思都是一样的。

RTT时间 x RTT次数 = 网络延迟时间

App端优化

如果你是一个多年iOS开发者,你应该会有一个体会,就是Apple一直在推动开发者使用一些新的网络协议,例如图中:IPv6、TLS1.3、HTTP/3。但其实很多公司服务端的更新迭代相对比较慢的,一个服务的升级往往需要经过很多论证才能推行。所以,作为整套系统其中一个子系统的App来说,对这些新的网络协议并不能按照建议第一时间实践。

作为开发者,有些可能没有机会真正实践上线,但是我们要做到 :可能不用我做,但我要知道

我们来看两张Session中的图

按照苹果的统计,Safari浏览器有20%的访问流量已经是基于HTTP/3承载的了,这个比例已经不低了,毕竟App端的HTTP/3是从iOS15才开始的。

从右图我们可以看到HTTP/3提升的标准RTT时间是比较大的,接近HTTP/1.1的一半,比HTTP/2也缩短了20%。

依照苹果的推荐,以下三种方式,都能不同程度的优化网络延迟,所以,在苹果看来使用新的网络协议在提升网络延迟方面有着关键总用。

那IPv6,TLS1.3, HTTP/3这些新的网络协议,他们又是如何起到优化网络的作用的,我们接下来探究一下。

IPv6

如下图:IPv6 使用的是固定报头,不像 IPv4 那样携带一堆冗长的数据,简短的报头提升了网络数据转发的效率。并且由于 IPv6 的路由表更小,聚合能力更强,保证了数据转发的路径更短,极大的提高了转发效率,IPv6 也消除了 IPv4 中常见的大部分地址冲突问题,并为设备提供了更多简化的连接和通信。

这里只介绍IPv6的传输效率特性,想了解其他如:更大的IP地址空间、更好的端到端体验、配置自动化程度更高、更高的安全性等等更多特性,可以浏览文章末尾的文献部分。

TLS1.2&TLS1.3

TLS 传输层安全是 IETF 在 SSL3.0 基础上设计的协议,相当于 SSL 的后续版本。TLS 主要分为两层,底层的是 TLS 记录协议,主要负责使用对称密码对消息进行加密。上层的是 TLS 握手协议,主要分为握手协议、密码规格变更协议和应用数据协议 4 个部分。

  • 握手协议:在客户端和服务端商定密码算法和共享密钥,包括证书认证,是 4 个协议中最复杂的部分

  • 密码规格变更协议:向通信对象传达变更密码方式的信号

  • 警告协议:发生错误的时候将错误传达给对方

  • 应用数据协议:将 TLS 承载的应用数据传达给通信对象的协议

握手协议是 TLS 协议中非常重要的协议,通过客户端和服务端的交互,共享必要信息,从而生成共享密钥和交互证书

一般情况下,不管 TLS 握手次数如何,都得先经过 TCP 三次握手后才能进行,因为 HTTPS 都是基于 TCP 传输协议实现的,得先建立完可靠的 TCP 连接才能做 TLS 握手的事情。但是TLS的握手次数要看版本,我们对比看下TLS1.2(上图)和TLS1.3(下图)的握手情况。

如上两张图,TLSv1.2 握手过程基本都是需要四次,也就是需要经过 2-RTT 才能完成握手,然后才能发送请求。在TLS1.3中,由于安全套件的减少,client可以在第一次请求中将5种安全套件全部生成一对密钥,将5种publicKey发送给server,然后server选择其中一种安全套件来生成自己的一对密钥。从而相比TLS1.2的握手过程减少了一次RTT

再再延伸一下,是不是所有的HTTPS一定都是先TCP握手,再TLS握手呢?

在特殊场景下,TCP的握手和TLS的握手会同时进行,但是有这特殊的发生条件:

  • TLS 版本得是 v1.3

  • 客户端和服务端都开启了 TCP Fast Open 功能

  • 客户端和服务端已经完成过一次TCP Fast Open通信

第1个和第3个条件,我们都理解了,那第2个是什么意思,这里我们再再再延伸一下TCP Fast Open和TLS1.3

TCP Fast Open

常规的情况下使用 TCP 传输协议进行通信,客户端和服务端通信之前,先要经过 TCP 三次握手后,建立完可靠的 TCP 连接后,客户端才能将数据发送给服务端。

其中,TCP 的第一次和第二次握手是不能够携带数据的,而 TCP 的第三次握手是可以携带数据的,因为这时候客户端的 TCP 连接状态已经是 ESTABLISHED,表明客户端这一方已经完成了 TCP 连接建立。

就算客户端携带数据的第三次握手在网络中丢失了,客户端在一定时间内没有收到服务端对该数据的应答报文,就会触发超时重传机制,然后客户端重传该携带数据的第三次握手的报文,直到重传次数达到系统的阈值,客户端就会销毁该 TCP 连接。

TCP Fast Open 则是为了绕过 TCP 三次握手发送数据,这个功能可以减少 TCP 连接建立的时延。要使用 TCP Fast Open 功能,客户端和服务端都要同时支持才会生效。不过,开启了 TCP Fast Open 功能,想要绕过 TCP 三次握手发送数据,得建立第二次以后的通信过程。

  • 客户端发送 SYN 报文,该报文包含 Fast Open 选项,且该选项的 Cookie 为空,这表明客户端请求 Fast Open Cookie;
  • 支持 TCP Fast Open 的服务器生成 Cookie,并将其置于 SYN-ACK 报文中的 Fast Open 选项以发回客户端;
  • 客户端收到 SYN-ACK 后,本地缓存 Fast Open 选项中的 Cookie。

所以,第一次客户端和服务端通信的时候,还是需要正常的三次握手流程。随后,客户端就有了 Cookie 这个东西,它可以用来向服务器 TCP 证明先前与客户端 IP 地址的三向握手已成功完成。

对于客户端与服务端的后续通信,客户端可以在第一次握手的时候携带应用数据,从而达到绕过三次握手发送数据的效果,整个过程如下图:

总结上面内容:如果客户端和服务端同时支持 TCP Fast Open 功能,那么在完成首次通信过程后,后续客户端与服务端 的通信则可以绕过三次握手发送数据,这就减少了握手带来的 1 个 RTT 的时间消耗。

TLS1.3 + TCP Fast Open

客户端和服务端同时支持 TCP Fast Open 功能的情况下,在第二次以后到通信过程中,客户端可以绕过三次握手直接发送数据,而且服务端也不需要等收到第三次握手后才发送数据。 如果 HTTPS 的 TLS 版本是 1.3,那么 TLS 过程只需要 1-RTT。

因此如果「TCP Fast Open + TLSv1.3」情况下,在第二次以后的通信过程中,TLS 和 TCP 的握手过程是可以同时进行的。

如果基于 TCP Fast Open 场景下的 TLSv1.3 0-RTT 会话恢复过程,不仅 TLS 和 TCP 的握手过程是可以同时进行的,而且 HTTP 请求也可以在这期间内一同完成。

HTTP/3

更快的连接

HTTP/2以前都是以TCP协议为载体,HTTP/3则是另辟蹊径,

HTTP/3是基于QUIC实现,它的底层协议是UDP,这么说似乎不太准确,HTTP 3.0 其实是拥抱了 QUIC 协议,而 QUIC 协议是建立在 UDP 协议基础上的。

QUIC 的握手连接更快,因为它使用了 UDP 作为传输层协议,这样能够减少三次握手的时间延迟。而且 QUIC 的加密协议采用了 TLS 协议的最新版本 TLS 1.3,相对之前的 TLS 1.1-1.2,TLS 1.3 允许客户端无需等待 TLS 握手完成就开始发送应用程序数据的操作,可以支持1 RTT 和 0 RTT,从而达到快速建立连接的效果。

更好的网络连接切换

在网络切换的场景下,以往我们需要重新建立连接,这个过程就相对比较耗时。苹果给我们提供了连接迁移的新方式,如下代码:

// For HTTP/3, enable on URLSessionConfiguration
let configuration = URLSessionConfiguration.default
configuration.multipathServiceType = .handover

// For QUIC, enable on NWParameters
let parameters = NWParameters.quic(alpn: ["myproto"])
parameters.multipathServiceType = .handover

multipathServiceType是一个枚举类型,定义了一系列场景使用网络的配置。App可以通过不同的策略来使用这些网络通道,该枚举配置的意义就是启动multipath,当主通道无法使用时,才会使用其他通道,启用handover正常工作,这样就实现了网络无缝切换的效果

QUIC datagrams

如果我使用基于UDP协议的网络,苹果推荐使用QUIC数据报,在QUIC协议的配置下,通过优化的拥塞控制算法可以降低RTT时间并减少丢包,代码如下:

// Create QUIC datagram flow
let options = NWProtoco1QUIC.Options ()
options.isDatagram = true

// Enable datagrams by setting maximum datagram frame size
let options = NWProtoco1QUIC.Options ()
options.maxDatagramFrameSize = 65535

当然这些代码得再iOS16及以上才能生效

服务端优化

检测工具

下面是几个session中推荐的几个检测工具

缓冲配置

在很多情况下,不合理或者说过大的缓冲配置会导致数据包在服务器侧形成巨大的缓冲队列,带来额外的延迟。苹果通过一个视频流媒体拖放的场景来对比了两种截然不同的配置下的表现:一种是相对过大的缓冲配置(TCP 4MB、TLS 256KB、HTTP 4MB),另外一种是苹果推荐的相对合理的经验配置(TCP 128KB、TLS 16KB、HTTP 256KB)。在过大的缓冲配置下,视频流媒体拖动播放的体验很差,需要等待较长的时间,视频才可以被继续播放。通过结合 macOS 中的网络质量检测工具的使用,我们可以很方便的定位到该问题,我们能够发现数据包在服务器侧的积压;减小缓冲配置到合理的数值后,该问题得到了极大的改善。

除了流媒体拖放场景,在其他类型的网络服务场景下同样可以尝试这个方法来改善网络状况。

L4S技术

L4S概念

本文的开头我们提到了网络较慢节点的数据包可能积压在队列中等待处理,从而导致网络传输收到影响。貌似无解的问题,现在有解了。

L4S技术就是来解决这个问题,它的目标就是来改善拥塞控制。L4S没有采用丢弃数据包的策略,而是提出了一种特定类型的数据包,这种数据包和传统的数据包在路由器上会使用两个不同的队列来分开处理,发送者会受到路由器设备关于网络拥塞的反馈,从而能够通过自适应调节发送速率来达到较少的排队效果。

要细说L4S我们先提一下ECN方案,这样便于我们理解。

在2001年标准化 的explicit congestion notification(ECN)协议是通过在不丢弃数据包的情况下通知发送者拥塞来改善上述问题的一个方案。ECN在IP报头中重新利用了两个bit:

0Transport is not ECN-capable
01ECN-capable ECT(1)
10ECN-capable ECT(0)
11Congestion experienced

ECT(0)和ECT(1)值具有相同的含义; 它们表明至少在这个IP包的发送方和接收方两端之间的route上的某个地方支持ECN。在实践中,所有实现都使用ECT(0)。ECN改善了拥塞方案,但还不够。一个问题是“Congestion experienced”的信号仍然来得太晚,拥堵已经发生了。它的处理措施也仍然很粗暴。ECN方案很出名,因为不少路由器会丢弃那些这两个bit不是00的数据包。

L4S的核心思想是用高级别协议中内置的更灵活的一些flag来取代ECN。为了实现这一点,L4S重新定义了上述ECT(1)值,表示“此数据包正在使用更好的拥塞通知”。然后路由器将创建两个单独的队列:L4S数据队列,速度较快;“经典”数据队列,速度较慢。这种改动,让人眼前一亮。目前开始应用,说明算法已经通过认证,得到了支持。

再来看看L4S的设计构思

效果对比

苹果的Session中以桌面同步的延迟来做了演示对比,对比的结果非常夸张,相差了十倍有余:

测试你的应用于L4S的兼容性

在iOS16上,你的开发者选项中会多一项L4S的开关,直接看图:

总结

本文针对Session 10078展开了对网络延迟解决办法的一些原理讨论和思路扩充。可能,在你的日常开发中不会遇到这些问题,或者这些问题不需要你去出面解决。但是我们需要掌握网络优化方面的基本知识,来解决或者协助解决App网络优化问题。通过阅读本文,你应该能够get到:

  • 客户端应该尽可能的使用苹果推荐的新的网络协议

  • 对于网络延迟的分析判断,做好RTT时间和RTT次数的探测

  • 对于服务端优化内容来说,你需要做好足够的知识储备,来推动服务端进行优化改造,因为这个在中大型公司中,都是跨团队或者跨部门的协作。推动者一般都要提供足够详细的改造方案,才有利于推动改造方案的实施。

参考文献

Session 10078 - Reduce networking delays for a more responsive app

Low Latency, Low Loss, Scalable Throughput (L4S) Internet Service: Architecture

mp.weixin.qq.com/s/KA66YEj5e…

IPv6

TLS 1.2

TLS 1.3

juejin.cn/post/684790…