1. Android 网络性能优化(1)概述
2. Android 网络性能优化(2)DNS优化
3. Android 网络性能优化(3)复用连接池
4. Android 网络性能优化(4)弱网优化
移动端时段,手机网络的连接形态是无线的,即通过Wifi连接,在前面章节有提高过,无线连接的优点就是便捷,只要有信号就能上网。而它的缺点是不稳定,它没有像导线那样的抗干扰手段,而且离信号越远网络越差。手机用户几乎都能体验到网络不好的情况的。
而对于移动端开发来说,在网络不好的情况下进行交互,如果处理不好,会消耗宽带、浪费电量等资源问题,所以我们有必要解决弱网环境下会出现的问题。弱网优化需要解决的核心问题有两点:
弱网并没有明确的定义,所以这需要我们根据实际场景来建立一套标准。一般来说有两个角度,即大指标:
上述两个都好理解,网络太复杂,造成丢包率高和网络延时高的原因太多了,比如:
既然没有明确的定义,那我们只能通过各种工具和手段来判断当前是否为弱网环境了。对于丢包率和网络延时,都是可以使用数据来进行衡量的,下面来介绍更细化的指标。
如下图所示:
httprtt = 接收第一个字节的时间 - 发送第一个字节的时间
。如果httprtt的时间较长,则说明出现了网络延时,也可能是接入的网络质量的问题。tcprtt = 通过tcp通道接收到第一个字节的时间 - 发送第一个字节的时间
。因为Http是基于Tcp的,所以在复用同一条tcp通道的情况下,httprtt的时间是包含tcprtt时间的。大部分情况下,httprtt已经可以说明问题的原因。吞吐量 = C / T
,C为完成的任务总量,T为完成这些任务的时间。在 百度APP移动端网络深度优化实践分享(三):移动端弱网优化篇中,百度使用的是一个循序渐进的过程,通过三个阶段来建立一个弱网标准:
腾讯的做法也大同小异,都是根据2.2的指标进行数据收集,整理一套达到弱网时的数据标准。但是我们该如何去探测这些数据呢?这需要我们实现一套完整的网络探测架构。
这部分基本照抄百度的实现了。网络探测是弱网检测的基础,目标是能够即时、正确的检测出网络质量,我们把网络探测分成两个部分,主动网络探测和被动网络采集。
主动检测,就是在触发了某些特定条件后,主动的进行网络检测,并按照一定的条件检查出是否为弱网状态。架构图如下所示:
策略层,通过多种策略,使主动探测的即时性和准确性得到提高。主要在网络请求成功和失败的时候触发弱网检测的逻辑。大致分成下面三个逻辑:
httprtt
,是否达到弱网的阈值(即设定的 weak-httprtt
),大于这个值就会进入弱网检测,为了防止频繁触发探测,加了时间间隔,一般为5~15minhttprtt
,是否没有达到阈值(即设定的 good-httprtt
),如果小于这个值则切回到正常的网络状态。防止频繁触发探测,加了时间间隔,一般为30s~60s。进入网络探测状态后,就轮到基础能力层来实现。
基础能力层就是提供网络探测的手段,一般有两个:
为什么要使用这两种手段呢?这是因为网络请求的过程中,主要有 DNS查询、TLS握手、TCP握手。而 DNS Query可以用来获取dns是否出现问题,ping可以用来检测TCP连接的连通性。
除了上面两种手段,应该也有其他网络探测的方式。在执行完这些手段后,将探测数据交给接口层。
接口层提供网络状态,大致为下面几个:
被动采集,就是每一次网络请求的所有细节都进行记录,并按照一定的条件将原始信息进行上报,上层再根据条件判断是否是弱网状态。cronet
(Chromium提供给Android的网络堆栈库)可以提供这个能力,OkHttp应该没有这种能力,所以可以考虑接入 cronet。
被动采集的信息主要有以下三个:
架构图如下所示:
基础能力层就是采集网络请求的三个参数tcprtt、httprtt、throughput。
(1)tcprtt采集
基于posix和windows的socket编程接口来获取tcprtt。获取时机在连接完成,读完成和写完成;
(2)httprtt采集
基于HTTP协议栈实现,通过计算接收response数据开始和开始发送的时间差,来获取httprtt。获取时机在读首包完成时;
(3)throughput采集
通过计算公式获取bytes总量和时间,基于posix和windows的socket编程接口来获取bytes。获取时机在读完成时记录接收的bytes,在写完成时记录发送的bytes。时间的获取在吞吐量管理模块里完成,获取时机在请求完成和请求销毁时。
通过获取到的数据进行上报,三种数据对应三种模块,对于上报的时机需要做处理。
(1) 套接字管理模块
使用cronet
的话,可以通过 getsockopt()
函数获取 tcp_info
结构体中的 trcpi_rtt
值。其次由于tcprtt的上报比较频繁,所以需要做时间限制,最少间隔 1~3s
(2)吞吐量管理模块
负责吞吐量计算,之前介绍了公式,从网络活动监控器模块获取bytes,但是吞吐量的计算单位是bits,所以将bytes乘以8,。只有GET请求会被列入统计计算,并且至少要累计5个请求才开始统计计算。
排除精准度的干扰,比如 localhost,私有子网上的主机,特定用途的子网主机,可以参考 rfc1918 的规范
(3)网络质量管理模块
从套接字管理模块获取tcprtt,从吞吐量管理模块获取吞吐量,并且在Http协议栈读到首包完成时获取httprtt。
获取到这三个值后,需要经过一些策略限制上报的频次,10s的间隔限制;网络类型不能是UNKNOWN;网络不能频繁切换;rtt和总吞吐量大小各300。
这些限制都是为了排除误差、降低性能损耗。
接口层提供的状态是对标主动采集的网络状态的,所以也包括 GOOD、BAD、UNKNOWN、OFFLINE
ConnectivityManager
得到 NetworkInfo
,判断网络信息(没有连接wifi、或者wifi无网络等)QUIC
是一种udp协议,由google发明,实现了TCP+TLS+HTTP/2
,它还有一个更加响亮的名称: Http/3
,Cronet库对其进行了支持,Android、iOS平台都可以使用。因为我自身对QUIC并不了解,所以这里并不会给出过多的使用介绍,接下来的一个月内,我会专门学习并写一篇有关 QUIC的文章。
总的来说,QUIC协议本身高效节流的特性,使得它在弱网络环境下可以大施拳脚。那么如何使用它呢?
在进行网络探测,并根据探测结果诊断当前网络状态为弱网时,网络请求进行切换,即将请求库切换到QUIC。
而当网络探测恢复为正常状态时,使用正常的网络库,例如OkHttp。
所谓QUIC的预连接,就是在进入弱网状态前提前建立QUIC连接。QUIC有引以为傲的0RTT,但第一次建立连接的时候是需要1RTT的,客户端首先会向服务器发送一个client hello消息,服务器会回复一个server reject消息,这个消息中包括了server config,有了server config后客户端就可以直接计算出密钥,完成0RTT。
所以我们在App启动的过程中会做一次QUIC预连接,将server config拉取下来,这样等进入弱网后走QUIC协议。
复合连接,即多条连接。它解决的场景是为了多个IP地址的连接选取问题。无论是HttpDNS还是LocalDNS中,我们都能获取到一个域名下的多个ip。我们可以找到最优ip进行连接:
比如我们通过HttpDNS
获取到两个ip,那我们将走下面步骤:
复合连接的好处是提供最优的ip选取机制,但是也会带来服务端的高负载,所以使用的时候需要进行综合评估。
弱网测试的本质是模拟弱网环境。通过 Fiddler
可以模拟。这里我介绍一种更便捷的方式: QNET
QNET弱网测试由腾讯开发,无需ROOT手机,无需连接数据线,以独立app的方式,为用户提供给快捷、可靠、功能完善的弱网络模拟服务,下载地址:QNET弱网测试
先选择需要测试的App:
然后设置参数,QNET提供了多个参数调整:
最后可以查看弱网模板:
百度APP移动端网络深度优化实践分享(三):移动端弱网优化篇
弱网的概念以及弱网测试
QUIC成为了HTTP/3的标准传输协议!
技术扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解
弱网的概念以及弱网测试