移动网络优化
第 8 章:无线网络的性能
首先,通过保活连接最小化延迟、将服务器和数据地理位置靠近客户端、优化 TLS 部署,以及我们涵盖的所有其他协议优化,在移动应用中只会更加重要——在移动环境中,延迟和带宽始终是稀缺资源。同样,所有 Web 应用性能最佳实践也同样适用。不妨先翻阅《Web 性能入门》一章;我们会等你回来。
然而,移动网络也为我们的性能策略提出了一些新的独特要求。为移动 Web 设计应用需要仔细规划,考虑内容呈现如何受限于设备形态、无线接口的独特性能特性,以及对电池寿命的影响。这三者紧密相连,不可分割。
也许是因为最容易控制,呈现层(如响应式设计等主题)往往获得最多关注。然而,大多数应用表现不佳,往往源于对网络性能的错误设计假设:应用协议虽然相同,但物理传输层的差异带来了诸多限制,若不加考虑,将导致响应缓慢、延迟波动剧烈,最终损害用户体验。更糟糕的是,糟糕的网络决策还会对设备电池寿命造成不成比例的负面影响。
这三个约束条件没有通用解决方案。呈现层、网络和电池寿命性能都有最佳实践,但它们常常相互矛盾;需要你和你的应用在需求中找到平衡。有一点是肯定的:简单地忽视其中任何一个都不会让你走得太远。
考虑到这一点,我们不会过多阐述呈现层,因为这随每个平台和应用类型而异——而且已有大量专著专门讨论此主题。但是,无论设备品牌或操作系统如何,移动网络带来的无线电和电池限制是普遍存在的,这正是本章要聚焦的内容。
在本章及后续页面中,“移动应用”一词采用最广泛的定义:我们关于移动网络性能的所有讨论同样适用于原生应用(无论平台)和在浏览器中运行的应用(无论浏览器厂商)。
节省电池电量
谈到移动设备,节能是所有相关方的关键关切:设备制造商、运营商、应用开发者和终端用户。当有疑问,或想知道某些移动行为为何或如何实施时,问一个简单的问题:它如何影响或改善电池寿命?事实上,这对应用中任何功能都是个好问题。
移动网络上的网络性能与电池性能内在关联。事实上,无线接口的物理层专门设计为在以下约束条件下优化电池寿命:
- 全功率使用无线电可在数小时内耗尽满电电池
- 每代无线技术的功率需求都在上升
- 无线电功耗通常仅次于屏幕
- 无线电使用具有相对于数据传输量的非线性能耗曲线
考虑到这一点,移动应用应尽量减少对无线接口的使用。需要明确的是,这不是说应该完全避免使用无线电;毕竟我们在构建依赖网络访问的联网应用!然而,由于保持无线电活跃对电池寿命代价高昂,我们的应用应在无线电开启时最大化传输数据量,然后尽量减少额外的数据传输次数。
尽管 WiFi 也使用无线电接口传输数据,但重要的是要认识到 WiFi 的底层机制,以及因此产生的延迟、吞吐量和功耗曲线,与 2G、3G 和 4G 移动网络有着本质区别;参见我们之前关于 3G、4G 和 WiFi 功耗需求的讨论。因此,在 WiFi 与移动网络上的网络行为可以且通常应该有所不同。
使用功耗分析工具测量能耗
尽管高度强调优化能耗,目前大多数平台仍缺乏帮助开发者测量和优化应用的必要工具。值得庆幸的是,有一些第三方工具可以提供帮助。
现代开发环境已大幅改进。Android Studio 内置的 Profiler 工具提供了详细的能耗监测功能,可以实时显示 CPU、网络、GPS 等组件的功耗情况。对于 iOS 开发者,Xcode Energy Gauge 和 Instruments 中的 Energy Log 模板提供了类似的分析能力。
跨平台开发者可以使用 Firebase Performance Monitoring,它不仅能追踪网络请求性能,还能帮助识别高能耗的操作模式。对于更底层的分析,Charles Proxy 和 Wireshark 可以捕获和分析网络流量,帮助识别低效的数据传输模式。
这些现代工具相比早期的单一解决方案(如 ARO)提供了更全面的视角,建议结合使用以获得最佳优化效果。
消除周期性低效的传输
移动无线电进入全功率状态会产生固定的功耗成本,无论要传输的数据量多少,这告诉我们:对电池而言,不存在”小请求”这回事。间歇性网络访问是移动网络上的性能反模式;参见《周期性传输的低效性》。事实上,延伸这一逻辑可得以下规则:
- 轮询在移动网络上代价极高;尽量减少
- 尽可能使用推送交付和通知
- 应合并和聚合出站和入站请求
- 非关键请求应推迟到无线电活跃时
一般来说,推送交付比轮询更高效。然而,高频推送流可能同样昂贵,甚至更昂贵。每当需要实时更新时,应考虑以下问题:
- 最佳更新间隔是多少?是否符合用户预期?
- 能否使用自适应策略代替固定更新间隔?
- 能否将入站或出站请求聚合为更少的网络调用?
- 能否将入站或出站请求推迟到稍后?
对于推送交付,原生应用可以使用平台特定的推送服务,应尽可能利用。对于 Web 应用,可以使用服务器发送事件(SSE)和 WebSocket 交付来最小化延迟和协议开销。尽可能避免轮询和高成本的 XHR 技术。
基于自适应间隔、用户偏好甚至设备电池电量,将多个通知捆绑到单个推送事件中的简单聚合策略,可以显著改善任何应用的功耗曲线,尤其是后台应用,它们常依赖此类网络访问模式。
Nagle 算法与高效服务器推送
TCP 爱好者无疑会将请求聚合和捆绑建议识别为 Nagle 算法,只是在应用层重新实现!Nagle 算法尝试将多个小 TCP 消息合并为单个数据包,以减少协议开销和线路上的数据包数量。毫不奇怪,我们的移动应用可以从利用相同技术中获益良多。
这种策略的简单实现是在服务器端按时间、计数或大小聚合消息,而不是为每个消息触发单独的推送。更复杂但显著更有效的策略是仅在客户端无线电已活跃时推送更新——例如,推迟消息直到客户端发起请求,或利用了解客户端无线电状态的第三方服务。
例如,Firebase Cloud Messaging (FCM)(已取代 Google Cloud Messaging)为 Android、iOS 和 Web 提供跨平台的消息交付 API,可以聚合消息并仅在设备活跃时交付更新:服务器将消息推送到 FCM,FCM 确定最佳交付时间表。
现代 Web 标准已大幅改进。Push API 和 Notifications API 现已得到广泛支持,允许 Web 应用接收推送通知,即使应用未在前台运行。对于实时通信,WebTransport 作为 WebSocket 的现代替代方案,提供了更高效的传输机制。
间歇性的信标请求(如受众测量 ping 和实时分析)可能轻易抵消你所有仔细的电池优化。这些 ping 在有线和 WiFi 网络上大多无害,但在移动网络上代价高昂。这些信标需要即时发生吗?很有可能你可以轻松记录并推迟这些请求,直到下次无线电活跃时。捎带你的后台 ping,并密切关注代码中第三方库和代码片段的网络访问模式。
最后,虽然我们迄今聚焦于电池,但渐进增强和增量加载等技术所需的间歇性网络访问也会因 RRC 状态转换带来巨大延迟成本!回想一下,每次状态转换在移动网络中都会产生高控制平面延迟成本,可能注入数百或数千毫秒的额外延迟——对用户发起和交互式流量来说尤其昂贵。
计算后台更新的能耗成本
为了说明周期性轮询对电池寿命的影响,我们做一些简单计算。这些数字不精确,但在典型 3G/4G 移动手持设备的范围内:
- 5 瓦时,或 18,000 焦耳的电池容量
- 将无线电从空闲循环到连接再返回消耗 10 焦耳能量
- 1 分钟轮询间隔每小时消耗 600 焦耳能量
- 600 焦耳能量约占总电池容量的 3%
单个应用每小时消耗约 3% 的可用电池容量!只需几个轮询间隔不重叠的应用,就能在中午前耗尽你的电池。不过,公平地说,频繁、未缓冲更新的推送应用可能具有更高的能耗曲线。
电池寿命优化与更新频率本质上是矛盾的。考虑你特定应用的需求以确定最佳策略:捆绑更新、自适应更新间隔、拉取 vs 推送等。然后,使用系统内置的能耗分析工具或类似工具测量影响并相应调整。
消除不必要的应用保活机制
连接状态和任何 TCP 或 UDP 连接的生命周期独立于设备上的无线电状态:无线电可以处于低功耗状态,而连接由运营商网络维护。然后,当外部网络的新数据包到达时,运营商无线电网络将通知设备,将其无线电提升到连接状态,并恢复数据传输。
应用不需要保持无线电”活跃”以确保连接不被丢弃。不必要的应用保活机制可能对电池寿命性能产生巨大的负面影响,通常是由于对移动无线电工作方式的简单误解而设置的。参见《物理层 vs 应用层连接》和《移动网络中的数据包流》。
大多数移动运营商设置 5-30 分钟的 NAT 连接超时。因此,你可能需要周期性(5 分钟)的保活来防止空闲连接被丢弃。如果你发现自己需要更频繁的保活,首先检查你自己的服务器、代理和负载均衡器配置!
现代移动操作系统已大幅优化后台连接管理。iOS 的 Background Fetch 和 Android 的 WorkManager 提供了系统级的后台任务调度,可以在系统认为合适的时间批量处理网络请求,避免应用自行维护频繁的保活连接。
对于 Web 应用,Service Workers 和 Background Sync API 允许浏览器代表应用管理后台同步,无需持续保持连接活跃。这些现代 API 比传统的定时轮询更高效,因为它们可以与系统级的电池优化策略协同工作。
预估网络延迟开销
在移动网络中,单个所需资源的 HTTP 请求可能产生数百到数千毫秒的网络延迟开销。部分原因是高往返延迟,但我们也不能忘记 DNS、TCP、TLS 和控制平面成本的 overhead!
在最佳情况下,无线电已处于高功率状态,DNS 已预解析,且已有可用的 TCP 连接:客户端可能能够复用现有连接并避免建立新连接的开销。然而,如果连接忙碌或不存在,那么我们必须在发送任何应用数据之前产生若干额外的往返。
为了说明这些额外网络往返的影响,我们假设乐观的 100 毫秒 4G 往返时间和 200 毫秒 3.5G+ 网络往返时间:
| 组件 | 3G | 4G |
|---|---|---|
| 控制平面 | 200–2,500 ms | 50–100 ms |
| DNS 查询 | 200 ms | 100 ms |
| TCP 握手 | 200 ms | 100 ms |
| TLS 握手 | 200–400 ms | 100–200 ms |
| HTTP 请求 | 200 ms | 100 ms |
| 总延迟开销 | 200–3,500 ms | 100–600 ms |
RRC 控制平面延迟单独就可以在 3G 网络上重新建立无线电上下文时增加数百到数千毫秒的开销!一旦无线电活跃,我们可能需要将主机名解析为 IP 地址,然后执行 TCP 握手——两个网络往返。然后,如果需要安全隧道,我们可能需要最多两个额外的网络往返(参见 TLS 会话恢复)。最后,可以发送 HTTP 请求,这至少增加另一个往返。
我们尚未考虑服务器响应时间或响应大小(可能需要若干往返),但我们已经产生了多达半打往返。乘以往返时间,我们看到 3G 网络的延迟开销达数秒,4G 网络约半秒。
考虑 RRC 状态转换
如果移动设备已空闲超过几秒,你应该假设并预期第一个数据包将产生数百甚至数千毫秒的额外 RRC 延迟。作为经验法则,为 4G 增加 100 毫秒,为 3.5G+ 增加 150-500 毫秒,为 3G 网络增加 500-2,500 毫秒,作为一次性的控制平面延迟成本。
RRC 专门设计用于帮助缓解操作耗电无线电的部分成本。然而,我们在电池寿命上的收益被延迟增加和吞吐量降低所抵消,这是由于各种定时器、计数器的存在,以及在不同无线电状态之间转换所需的网络协商开销。然而,RRC 也是移动网络上的现实——无法回避——如果你想为移动 Web 构建优化应用,必须在设计时考虑 RRC。
关于 RRC 的快速总结:
- RRC 状态机因每个无线标准而异
- RRC 状态机由无线电网络为每个设备管理
- 需要传输数据时发生 RRC 状态提升到高功率
- 网络配置的定时器超时后发生 RRC 状态降级到低功率
- (4G) LTE 状态转换可能需要 10 到 100 毫秒
- (4G) HSPA+ 状态转换与 LTE 相当
- (3G) HSPA 和 CDMA 状态转换可能需要数秒
- 每次网络传输,无论大小,都会产生能耗尾部
我们已经介绍了为什么节省电池对移动应用如此重要,也强调了间歇性传输的低效性,这是由定时器驱动的 RRC 状态转换的直接结果。然而,还有一点需要记住:如果设备无线电已空闲,那么在移动网络上发起新数据传输将产生额外的延迟,在最新一代网络上可能需要 100 毫秒,在较旧的 3G 和 2G 网络上可能长达数秒。
虽然网络向我们的应用呈现永远在线的假象,但由 RRC 控制的物理或无线电层却在持续连接和断开。表面上看,这不是问题,但 RRC 施加的延迟实际上往往很容易被许多用户察觉,如果未加考虑的话。
解耦用户交互与网络通信
设计良好的应用即使底层连接缓慢或请求需要很长时间完成,也能通过提供即时反馈而感觉快速。不要将用户交互、用户反馈和网络通信耦合在一起。为了提供最佳体验,应用应在数百毫秒内确认用户输入;参见《速度、性能与人类感知》。
如果需要网络请求,则在后台发起,并提供即时 UI 反馈以确认用户输入。仅控制平面延迟就常常会将你的应用推出提供即时用户反馈的预算。为高延迟做计划——你无法”修复”核心网络和 RRC 施加的延迟——并与设计团队合作,确保他们在设计应用时了解这些限制。
为可变网络接口可用性设计
用户不喜欢慢应用,但由于瞬态网络错误导致的崩溃应用是最糟糕的体验。你的移动应用必须对常见网络故障具有鲁棒性:不可达的主机、吞吐量的突然下降或延迟的增加,或完全失去连接。与有线世界不同,你不能假设一旦建立连接就会保持连接。用户可能在移动,可能进入高干扰区域、活跃用户众多或信号覆盖差的区域。
此外,就像你不能仅为最新浏览器设计页面一样,你也不能仅为最新一代移动网络设计应用。正如我们之前介绍的(《构建多代未来》),即使使用最新手持设备的用户也会根据无线电环境的持续变化条件,在 4G、3G 甚至 2G 网络之间连续切换。你的应用应该订阅这些接口转换并相应调整。
应用可以订阅网络状态通知来监控连接状态。对于 Web 应用,使用 Network Information API 获取有关连接类型的信息(尽管浏览器支持仍有限)。对于更可靠的状态检测,结合使用 navigator.onLine 和实际的网络请求探测。
移动网络中唯一不变的是变化。无线信道质量总是基于与塔的距离、附近用户的拥塞、环境干扰和数十个其他因素而变化。考虑到这一点,虽然进行各种形式的带宽和延迟估计以优化移动应用可能很诱人,但结果应充其量视为瞬态数据点。
iPhone 4 “天线门”事件很好地说明了无线电性能的不可预测性:接收质量受手相对于手机天线的物理位置影响,这催生了著名的”你握持方式不对”的说法。
移动网络上的延迟和带宽估计在数十到数百毫秒的范围内稳定,最多一秒,但不会更长。因此,虽然自适应码率流等优化对于长流(如视频)仍然有用,它适应跨越几秒的数据块,但这些带宽估计绝对不应该被缓存或用于以后做出关于可用吞吐量的决策:即使在 4G 上,你可能测得吞吐量仅为几百 Kbit/s,然后将无线电移动几英寸就能获得 Mbit/s+ 的性能!
移动网络上的流式应用
移动网络上的流式应用是个棘手问题。如果你需要执行大下载且确信整个文件都会被使用,那么你应该一次性下载整个文件,然后让无线电尽可能长时间保持空闲——例如,我们之前介绍的 Pandora 应用中音乐文件下载的行为。
然而,如果由于大小或用户行为限制无法流式传输完整文件(例如高清视频),那么你应该利用自适应码率流来持续适应网络吞吐量的变化。你将承担高电池成本,但至少能在这样做时提供最佳用户体验!或者,考虑提示用户切换到 WiFi。
端到端带宽和延迟估计在任何网络上都是难题,在移动网络上更是难上加难。避免这样做,因为你可能会出错。相反,使用关于网络代际的粗粒度信息,并相应调整你的代码。需要明确的是,知道移动网络的代际或类型并不能做出任何端到端性能保证,但它确实告诉你关于第一无线跳延迟和运营商网络端到端性能的重要数据;参见《移动网络中的延迟和抖动》和表 7-6。
最后,除了吞吐量和延迟,你应该为失去连接做计划:假设这种情况不是例外而是常态。你的应用应在网络不可用或发生瞬态故障时尽可能保持运行,并应根据请求类型和特定错误进行调整:
- 不要缓存或尝试猜测网络状态
- 分发请求,监听故障,并诊断发生了什么
- 瞬态错误会发生;为它们做计划,并使用重试策略
- 监听连接状态以预期最佳请求策略
- 对请求重试使用退避算法;不要永远旋转
- 如果离线,记录并在可能时稍后分发请求
- 利用 Service Workers、Cache API 和 IndexedDB 实现离线模式
随着 HetNet(异构网络)基础设施的日益普及,小区切换的频率将大幅上升,这使得监控连接状态和类型变得更加重要。
突发传输后迅速回归空闲
移动无线电接口针对突发传输进行了优化,这是你应该尽可能利用的特性:将请求组合在一起,尽可能快地下载尽可能多的数据,然后让无线电返回空闲状态。这一策略将提供最佳网络吞吐量并最大化设备电池寿命。
估计网络速度的唯一准确方法是,嗯,使用它!最新一代网络(如 LTE 和 5G)在一毫秒间隔内动态分配资源,并优先处理突发数据流。为了快速传输,保持简单:尽可能批量和预取数据,让网络处理其余部分。
一个重要的推论是,在移动网络上渐进式加载资源可能弊大于利。通过分小块下载内容,我们使应用面临更高的吞吐量和延迟可变性,更不用说操作无线电的更高能耗成本。相反,预测用户接下来需要什么,提前下载内容,让无线电空闲:
- 如果需要获取大型音乐或视频文件,考虑预先下载整个文件,而不是分块流式传输
- 预取应用内容,并投资于指标和统计模型,帮助识别哪些内容适合提前下载
- 预取第三方内容(如广告),并添加应用逻辑在必要时显示和更新其状态
- 消除不必要的间歇性传输
构建和评估预取模型
内容预取总会产生自然的张力:一方面,你想下载尽可能少的字节,另一方面,你想最小化延迟和吞吐量可变性并减少对电池的影响。哪个更重要?错误的问题。答案总是取决于你的应用和你选择用来确定预取策略有效性的指标。
重要的要点是,至少有三个变量需要平衡:传输字节数、对电池的影响、网络吞吐量和延迟的可变性。此外,正如我们所见,这些变量并非互斥:在单次传输中传输更大批量的字节可能给你带来更好的吞吐量。
具有高度可预测使用模式的应用可以利用激进的预取,最小化电池消耗,改善用户体验,同时避免产生大的字节开销。相反,实施不当的预取策略可能下载大量不必要的数据,损害用户的整体体验。
要确定你的应用应该如何表现,首先确定你的主要目标和应用的主要使用模式。然后使用这些数据实施预取策略并收集指标以验证模型的假设。重复、迭代、优化。
卸载到 WiFi 网络
当前行业估计显示,全球近 90% 的无线流量预计来自室内,且经常在 WiFi 覆盖范围内。因此,虽然最新的 5G 网络可能在峰值吞吐量和延迟上与 WiFi 竞争,但它们通常仍施加了月度数据上限:移动访问是计量的,对用户来说往往很昂贵。此外,WiFi 连接对大型传输更节能(参见 3G、4G、5G 和 WiFi 功耗需求)且不需要 RRC。
尽可能利用 WiFi 连接,特别是如果你在构建数据密集型应用,应在可用时利用 WiFi 连接,如果没有,则考虑提示用户在设备上启用 WiFi 以改善体验并最小化成本。
应用协议和最佳实践
我们网络基础设施的分层架构的一个伟大特性是,它将物理交付从传输层抽象出来,传输层又将路由和数据交付从应用协议抽象出来。这种分离提供了出色的 API 抽象,但为了最佳端到端性能,我们仍需考虑整个协议栈。
在本章中,我们聚焦于移动网络物理层的独特属性,如 RRC 的存在、对设备电池寿命的担忧,以及移动网络中产生的路由延迟。然而,在此物理层之上坐落着我们在前面章节介绍的传输和会话协议,它们的所有优化同样关键,或许更加关键:
- 优化 TCP
- 优化 UDP
- 优化 TLS
- 优化 QUIC/HTTP3
通过复用保活连接最小化延迟、将服务器和数据地理位置靠近客户端、优化 TLS 部署,以及我们之前介绍的所有其他优化,在移动网络上更加重要,那里往返延迟高且带宽始终是稀缺资源。
当然,我们的优化策略不止于传输和会话协议;它们只是基础。从那里,我们还必须考虑不同应用协议(HTTP/1.1、HTTP/2、HTTP/3)的性能影响,以及一般 Web 应用最佳实践——继续阅读,我们还没讲完!