Reduce network delays for your app
System & Services 进阶 20m

减少你的 App 网络延迟

Reduce network delays for your app

2021年6月10日

在 Apple 官方观看视频

一句话判断

网络延迟优化的最大收益不在代码层面——启用 HTTP/3、配置 DNS 预解析、复用连接这三招能砍掉 50% 以上的延迟。

这场 Session 讲了什么

Session 从系统层面讲解了如何减少 App 的网络延迟。核心观点是:大部分网络延迟不是你的代码慢,而是协议和连接管理不够优化。

三大优化方向:一是启用 HTTP/3(基于 QUIC 协议)。HTTP/3 在传输层使用 UDP 代替 TCP,消除了 TCP 的队头阻塞问题(Head-of-Line Blocking),在丢包的网络环境下性能提升明显。iOS 15 和 macOS Monterey 的 URLSession 默认支持 HTTP/3,你只需要服务端支持。

二是 DNS 预解析(DNS Prefetching)。在用户可能发起请求之前提前解析域名,省掉首次请求时的 DNS 查询时间。

三是连接复用和资源调度。避免多个请求串行发送,用 HTTP/2 的多路复用或并发请求最大化带宽利用率。

值得深挖的点

HTTP/3 的队头阻塞消除

HTTP/2 解决了 HTTP 层的队头阻塞(一个 TCP 连接上可以并行多个流),但 TCP 层的队头阻塞仍然存在——一个 TCP 包丢了,后面所有的包都得等。HTTP/3 的 QUIC 协议在 UDP 之上实现了独立的流,每个流的丢包只影响自己所在的流,不影响其他流。在移动网络(丢包率 1-3%)环境下,这意味着一个请求的丢包不会拖慢同时进行的其他请求。

DNS 预解析的时机

DNS 解析通常需要 20-200ms(取决于 DNS 服务器的距离)。如果你在用户点击按钮时才开始 DNS 解析,这 200ms 就加到了用户感知的延迟中。iOS 15 允许你通过 NWConnection 的参数配置预解析——在 App 启动时或用户浏览列表时就解析好可能用到的域名。

代码片段

启用 HTTP/3

import Network

// 使用 URLSession 自动支持 HTTP/3
// 只需在服务端配置 Alt-Svc header:
// Alt-Svc: h3=":443"; ma=86400

let config = URLSessionConfiguration.default
// iOS 15 默认支持 HTTP/3,无需额外配置
// 只要服务端 advertise 支持,URLSession 会自动升级
let session = URLSession(configuration: config)

使用 Network 框架进行 DNS 预解析

import Network

// 预解析域名
func prefetchDNS(host: String) {
    let endpoint = NWEndpoint.hostPort(
        host: NWEndpoint.Host(host),
        port: 443
    )
    
    let parameters = NWParameters.tcp
    let connection = NWConnection(to: endpoint, parameters: parameters)
    
    // 只需要启动连接的解析阶段
    connection.stateUpdateHandler = { state in
        if case .ready = state {
            // DNS 解析完成,连接已建立
            connection.cancel()
        }
    }
    connection.start(queue: .global())
}

并发请求最大化带宽利用率

// 错误:串行请求
// func loadAll() async throws {
//     let user = try await fetchUser()       // 等完成
//     let feed = try await fetchFeed()       // 再等
//     let config = try await fetchConfig()   // 再等
// }

// 正确:并发请求
func loadAll() async throws -> (User, [FeedItem], Config) {
    // async let 让三个请求同时发出
    async let user = fetchUser()
    async let feed = fetchFeed()
    async let config = fetchConfig()
    
    // 等待所有结果
    return try await (user, feed, config)
}

最佳实践

基础设施: 联系你的后端团队启用 HTTP/3(nginx、Cloudflare、AWS ALB 都已支持)。在响应头中添加 Alt-Svc 告知客户端支持 HTTP/3。配置 CDN 的边缘节点离用户更近。

App 层面: 在 App 启动时预解析核心 API 域名。使用 async let 并行化独立的网络请求。避免在 UITableViewCell 的 cellForRowAt 中同步发起请求——提前批量加载。

还有什么值得关注

  • iOS 15 的 URLSession 在弱网环境下自动重试失败请求。
  • NWConnection 的 pathUpdateHandler 可以监控网络状态变化,在 Wi-Fi 和蜂窝之间切换时自动调整。
  • Instruments 的 Network 工具可以可视化展示 HTTP/3 和 HTTP/2 的性能差异。
WWDC 2021