通过优化App来适应网络(WWDC 2018 session 719)

该篇博客记录了观看WWDC Session714《Optimizing your app for today’s internet》的内容以及一些理解。

全球移动数据

一、全球有大约40亿人在使用互联网

  1. 这个数字超过了世界人口的一般
  2. 在全球范围内,使用人数的增长率正在减缓

二、互联网整体还是发展的

  1. 机器到机器、物联网、只能家具都在发展
  2. 中国、印度互联网也在发展
  3. 智能手机、移动数据也在发展

关注不太理想的网络情况

如果一个应用是在快速的LTE网络环境下构建的,那么它可能在2G移动网络环境下会表现的很糟糕。

但是如果一个应用是在2G移动网络环境下构建的,那么它在快速的LTE网络环境下也会表现的很好。

所以我们应该多关注网络情况较差的情况,对于这种情况,有以下几点建议:

  • 2G移动网络在全球范围内依旧是很普遍的
  • Network Link Conditioner(NLC)是一个很好的工具,它可以模拟接近于真实世界的网络状况
  • 使用工具(例如:Wireshark和tcptrace)来帮助分析问题
  • 应该从一开始就构建不同网络环境,如果在最后再去构建不同网络环境,那么此时已经晚了

IPv6的可用性

IPv6是十分重要的,因为它比IPv4有更好的性能。

如果要让App有更好的性能,我们不仅需要确保App的性能,还要确保App连接的服务器支持IPv6。

IPv6的使用在全世界一直呈现增长的趋势:
Percentage of connections made on a network that offered IPv6 connectivity

下面是一组Apple统计的关于印度的网络数据:

India Cellular RTT Values

由图中数据可以看出,在使用IPv6的情况下,75%的TCP连接可以在150ms内建立;而使用IPv4的情况下,建立连接可能耗时325ms以上,由此可见IPv6对于性能的提升。

Explicit Congestion Notification(ECN)

我们可以使用Explicit Congestion Notification(ECN)来提高性能,ECN是通过减少数据包丢失和重传技术来提高性能的。

默认情况下,macOS和iOS已经启用了这个功能,所以我们不需要在应用上做额外的事情来启用该功能。我们唯一需要做的就是确保我们的后台支持ECN

以下是对于Alexa前百万服务支持ECN占比的调查数据:

Survey of Alexa top million web sites

Multipath TCP(MPTCP)

我们在使用TCP时,如果遇到了网络切换,可能会导致连接的断开。

MPTCP可以根据每个数据包来进行路由决策,进而实现实时切换连接,解决该问题。

关于MPTCP,有以下几个要点:

  • 更快的连接故障切换
  • 与代理一起使用
  • 从2013年开始在端到端的服务器上使用
  • 依赖于移动运营商,全球78%的运营商支持MPTCP,只有22%的运营商不支持MPTCP

TCP Fast Open(TFO)

  • 可以避免TCP三次握手连接建立时间
  • 需要确认运营商提供的服务器支持TCP Fast Open

Quick UDP Internet Connections (QUIC)

一、QUIC是一种新的传输协议

  1. 可能接替TCP
  2. 运行在无法辨识的资料包层面
  3. 提供可靠的、噪音可控的数据流

二、标准化的IETF项目正在进行中

三、苹果的工程师参与了该项目的开发

四、QUIC一旦准备好,Apple就会提供支持它的API

DNS

DNS的性能

现在的DNS有如下特点和缺陷:

一、许多网站使用较短生命周期的DNS记录

  1. DNS生命周期低于60s
  2. 这么做支持快速故障处理

二、现在的数据中心很少有down掉的情况

  1. 很少使用快速故障处理
  2. IP地址通常不会更改
  3. 由于DNS记录生命周期很短且IP地址通常不会更改,可能造成无故的250ms的延迟
  4. 一些ISPs缓存DNS的时间要比60s长

DNS优化

优化的思路为:如果要求DNS记录,Apple会立刻返回记录,无论该记录是否过期;如果记录已经过期,那么Apple会异步的进行新的DNS记录的查询;如果查询结果与过期结果一致,那么就提升了效率,如果查询结果与预期结果不一致,那么就异步通知程序新的地址。

关于DNS的优化,以下是几个重要的点:

一、CloudKit目前已经使用该优化

二、可以通过在DNSServiceQueryRecord或者DNSServiceGetAddrInfo中设置kDNSServiceFlagsAllowExpiredAnswers来启用优化

三、该优化需要配合Happy Eyeballs(RFC 6555)算法进行

  1. 如果拥有记录,那么就尝试去连接
  2. 如果DNS报告了新的地址,那么也尝试连接新地址

Avoid SCNetworkReachability

我们经常使用 SCNetworkReachability 来预测网络,一般步骤为:

  1. 通过 SCNetworkReachability 来检查网络
  2. 网络检查通过,尝试去连接
  3. 在此过程中,网络可能会发生变化,导致连接失败,就会再次进行第一步

上述问题主要是由于设计不合理,在上述设计中,我们做的是预测网络是否可达,但是预测网络是一件不靠谱的事情。

在iOS11中,为URLSession.config提供了waitsForConnectivity属性,该属性表明只有系统在确认网络通畅的情况下才会进行请求,进而避免了请求失败再次开启请求的问题。

Transport Layer Security (TLS) 1.3

TLS1.2已经发布10年了,现在TLS1.3已经准备好了,下面是TLS1.3的一些要点:

  • 提高了安全性
  • 缩短了建立连接的时间
  • IESG(互联网工程指导小组)在2018年3月21日批准了最终草案
  • 今年夏天将作为IETF RFC被RFC编辑队列发布
  • 最终版本已经在iOS 12中测试过了
  • 现在在iOS或macOS中默认关闭TLS1.3,可以使用sudo defaults write /Library/Preferences/com.apple.networkd tcp_connect_enable_tls13 1提前打开TLS1.3

Certificate Transparency

我们可能会遇到证书颁发机构处于恶意或无能,向实体颁发了不该颁发的证书,这样会引起安全问题。

解决这一个问题的方法是一个叫做证书透明日志的东西,该物具有以下特点:

  • 证书透明日志是对已经颁发证书的公共验证日志
  • 任何人都可以访问该日志
  • 客户端可以检查该证书是否被记录

如果一个流氓证书颁发机构颁发虚假证书,如果它要记录日志,那么就会被发现它在颁发虚假证书;如果它对该证书不进行记录,那么客户端不会通过验证。

它的工作流程如下:

Certificate Transparency

  • 该策略从2018年末开始:所有公开的受信任的CA机构颁发的新的TLS证书必须能够被Certificate Transparency验证
  • 现有的已经颁发的证书不受影响
  • 客户端不受影响

Bonjour Conformance Test

Bonjour Conformance Test可以验证你的硬件设备是否正确实现Bonjour。

Bonjour Conformance Test验证被要求使用的场景

  • 硬件产品上使用了Bonjour名称和logo
  • 使用Windows应用程序绑定Windows安装包的Bonjour
  • AirPrint、AirPlay、CarPlay以及HomeKit设备

Bonjour Conformance Test的好处

  • 帮助提高产品的可靠性
  • 提高用户体验

API Choices

现在我们的应用中,可能使用URLSession来访问网络,也可能使用第三方框架访问网络,第三方框架可能是通过BSD Socket来访问网络,同样的,我们的应用也可以直接使用BSD Socket来访问网络。如下:

API Choices

Apple开始开源URLSession底层的Network.framework框架,并且强烈建议我们在程序中使用URLSession或直接使用Network.framework,同时也建议第三方框架提供商也改用Networ.framework,避免使用BSD Sockets。如下:

API Choices

URLSession

  1. URLSession是一个高级基础网络API,它支持HTTP/2、HTTP/1.1;支持过程中和过程外的转移;支持处理cookies、缓存、身份验证和代理;提供URLSessionStreamTask用于TCP连接

  2. URLSession是所有Apple平台上推荐的API

接下里从以下四个方面讨论下URLSession的细节:

  • 延迟(Latency)
  • 吞吐量(Throughput)
  • 响应能力(Responsiveness)
  • 系统资源(System resources)

延迟(Latency)

减少延迟

HTTP 1.1

如果我们现在需要向服务器请求三份资源,在使用HTTP 1.1的情况下,整体流程如下:

HTTP 1.1

在这个过程中,我们耗费许多资源去重复打开新连接,如果我们改用单个连接会怎么样?

单个连接

使用单个连接的过程如下:

Single Connection

在这个过程中,我们可以看到,我们必须等到前一个请求得到响应之后才能开启第二个请求,这就叫做HTTP线头阻塞(HTTP head-of-line blocking)

HTTP 2

我们改用HTTP 2来实现该次需求,过程如下:

HTTP 2

在这个过程中,HTTP/2减少了每个请求之间的间隔,同时减少了等待响应的空闲时间。

HTTP/2具有以下优点:

  • 在HTTP层没有HTTP线头阻塞
  • 更好的带宽利用率
  • 无需客户端更改(前提是使用URLSession)
  • 减少服务器端开销

HTTP/2 Connection Coalescing

从今年开始URLSession引入了HTTP/2 Connection Coalescing来减少连接。

URLSession采用了新的连接策略:当 1.IP地址匹配或者2.连接由同一个TLS证书覆盖的主机时,URLSession将只开启一个连接。

新的策略在iOS 12macOS Mojave中启用。

URLSession原有的策略如下:
Old behavior

URLSession新的策略如下:
New behavior

新策略的优点

  • 连接重用
  • 对HTTP/2特别重要
  • 减少创建URLSession对象昂贵的消耗

吞吐量(Throughput)

最大化吞吐量

原理:减少请求大小

HTTP cookies

  • 指定好域和路径
  • 使用较小cookies
  • 删除不想要的cookies或者为cookies设置过期时间
  • 适当的在服务器上保存一些状态

HTTP/2报头压缩

URLSession支持两种压缩方式

Gzip
  • 支持HTTP和HTTPS压缩
Brotli
  • iOS 11和macOS High Sierra之后推出
  • 仅支持HTTPS压缩
  • 针对文本和HTML做了优化
  • 对短数据压缩率最好

响应能力(Responsiveness)

提升响应能力

QoS(Quality of Service)

首先我们看一下QoS类别,由高到低分别为:

  • 用户交互的(User-Interactive)
  • 用户创建的(User-Initiated)
  • 默认的(Default)
  • 实用程序的(Utility)
  • 后台的(Background)

URLSession对于QoS的结合

  • URLSession对于QoS是有感知的
  • 当调用task.resunme()时,会获取Dispatch queue的QoS
  • Delegate的消息遵循QoS

Network Service Type

在今年,会新增一种responsiveData级别的类型:
Network Service Type

  • 大多数情况使用default和background
  • 当使用Cisco Fast Lane时,会自动标记为responsiveData

URLSession Adaptable Connectivity

  • 之前,我们只要有请求就会立刻进行连接
  • 允许每一个URLSession使用waitsForConnectivity
  • 如果资源不是需要资源,那么在回调func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask)中调用task.cancel()

系统资源(System resources)

更好地利用系统资源

Use Background Sessions

  • 要利用系统智能的开启与暂停任务
  • 如果任务没有完成,即使App没有运行,那么传输也在执行

使用后台Session的代码如下:

// Optionally set isDiscretionary for non-urgent downloads
let configuration = URLSessionConfiguration.background(withIdentifier: "com.apple.example")
configuration.isDiscretionary = true

Cache Wisely

  • 缓存应该是暂时的
  • 不要缓存唯一的内容
  • 考虑在以下方法中决定是否对某个内容进行缓存
// Implement delegate method to decide when to cache content
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask,
 willCacheResponse proposedResponse: CachedURLResponse,
 completionHandler: @escaping (CachedURLResponse?) -> Void) {
 // If you don't want to cache
 completionHandler(nil)
}
  • 如果可能,服务端通过Cache-Control: no-store来决定是否缓存

总结

以下总结今天提到的优化方式以及对优化入手方面的效果:

Recap

猜你喜欢

转载自blog.csdn.net/TuGeLe/article/details/81459886
今日推荐