1.主动网络探测
所谓主动探测,就是在触发了某些条件后,主动的进行网络探测,并按照一定的条件检查出是否是弱网状态。百度App自研了主动探测组件,如下图所示。
1.1策略层
探测策略层通过多种策略的组合,使主动探测的即时性和准确性得以大大提高,我们结合上面的策略层图来解释下检测维度的意义。
我们分别在网络请求成功和失败的时候触发了弱网检测的逻辑。主要分为如下三种逻辑。
1)成功时,如何判断进入弱网状态?检查weakhttprtt的阈值,这个值取决于业务的设置(一般这个值会针对特殊场景的请求取95分位或者更大分位的值),大于这个值就会进入弱网检测,为了防止频繁触发探测加了时间间隔维度,目前定义的是10分钟。从线下模拟测试来看,只要大于这个阈值,检测结果必然是弱网状态。
2)成功时,如何判断退出弱网状态?检查goodhttprtt的阈值,这个值取决于业务的设置(一般这个值会取整体网络的95分位或者更大分位的值),小于这个值证明要切换回正常网络状态,为了防止频繁触发探测加了时间间隔维度,目前定义的是30秒。从线下模拟测试来看,只要小于这个阈值,检测结果必然是正常状态。如果大于或者等于这个阈值,也不能证明一定不是正常网络,所以也需要发起网络探测,但是由于这是在成功回调里,频次会很高,所以我们加上时间间隔的限制30秒,还加入了次数的限制,连续成功次数%次数阈值(4次)等于0。但这看起来还是频次有点高,所以我们引入了阶梯递增机制,随着次数的增长,成60秒几何倍数增长。
3)失败时,如何判断进入弱网状态?首先会判断连续失败次数,连续失败次数/次数阈值(2次)等于1并且连续失败次数%次数阈值(2次)等于0,相比成功,失败的次数检查较为苛刻,主要还是考虑多次触发网络检测损耗性能。
进入弱网状态后,就会触发基础能力层的ping和dns query的探测。
1.2基础能力层
探测基础能力层,主要提供弱网检测的手段,一是dns query,一是ping,百度App使用C++实现了这两个能力。为什么要选用这两种手段呢?我们在系列二中介绍过,一个网络请求,分为DNS-》TLS-》TCP-》数据传输 四个阶段。想判定网络连通性主要在DNS和TCP阶段,所以dns query和ping就是用来检测这两个阶段的连通性手段。dns query向百度核心域名mbd.baidu.com发起dns查询,查询的DNS服务器为系统配置的DNS服务器(iOS通过res_ninit函数构建一个__res_state的结构体,Android通过systemproperty获取net.dns1和net.dns2的值,便可获取系统配置的DNS服务器),DNS查询的超时时间为3s。ping的目标地址为百度核心域名mbd.baidu.com,ping的次数为两次,每次超时时间是默认的1s。
判断出弱网状态后,会将结果提供给接口层。
1.3接口层
接口层主要提供主动探测出来的网络状态,目前包括GOOD,BAD,UNKNOWN,OFFLINE。
1)GOOD:dns查询成功并且ping也成功,即标记为GOOD状态。
2)BAD:ping失败一次标记为BAD状态。
3)UNKNOWN:初始状态或者识别不出来状态为UNKNOWN状态。
4)OFFLINE:dns server错误(没有获取到要发送的DNS server地址),网关错误(读取/proc/net/route文件内容失败),发送dns错误(发送dns数据出错),ping读写错误(ping的过程中读写错误),接收dns错误(接收dns数据出错),ping地址错误(ping地址是空),dns未知域名错误(dns没有查询到域名错误),初始化icmp错误(初始化icmp失败),dns udp错误(创建UDP socket失败),即标记为OFFLINE状态。
2.被动网络采集
所谓被动采集,就是每一次网络请求的所有细节都进行记录,并按照一定的条件将原始信息进行上报,上层再根据条件判断是否是弱网状态。百度App基于cronet的NQE(Network Quality Estimator)进行了二次订制开发。
首先我们讲解下需要采集的数据,包括tcprtt、httprtt、throughput三个维度,如下图所示。
1)tcprtt,基于posix和windows的socket编程接口来获取tcprtt。获取时机在连接完成,读完成和写完成。
2)httprtt,基于HTTP协议栈实现,通过计算接收response数据开始和开始发送的时间差,来获取httprtt。获取时机在读首包完成时。
3)throughput,通过上面的计算公式需要获取bytes和时间,基于posix和windows的socket编程接口来获取bytes。获取时机在读完成时记录接收的bytes,在写完成时记录发送的bytes。时间的获取在吞吐量管理模块里完成,下面会讲到。获取时机在请求完成和请求销毁时。
如下为被动网络采集的整体架构图。
1.1能力层
能力层内容上面我们已经讲过,主要采集tcprtt、httprtt、throughput三个维度的数据。
1.2策略层
被动采集策略层通过多种策略的组合,降低各种采集数据的上报时机,降低性能的影响。
1)套接字管理模块,首先负责获取tcprtt的值,如何获取tcprtt呢?通过getsockopt函数获取tcp_info结构体里的tcpi_rtt值。其次由于tcprtt的上报频次比较频繁,所以做了1秒的时间间隔上报限制。
2)吞吐量管理模块,负责吞吐量的计算,上面介绍了计算公式,从网络活动监控器模块获取bytes,但吞吐量的计算单位是bits(位),所以将bytes乘以8。只有GET请求会被列入统计计算,并且至少要累计5个请求后才能开始统计计算。排除精准度的干扰,比如localhost,私有子网上的主机,特定用途子网主机,可参考RFC1918。
3)网络质量管理模块,从套接字管理模块获取tcprtt,从吞吐量管理模块获取吞吐量,并且在HTTP协议栈读首包完成时获取httprtt。获取到这三个值后,需要经过一些策略限制上报的频次,10秒间隔的限制;网络类型不能是UNKNOWN(1.3的第三部分会详细讲解);网络不能频繁切换;rtt和吞吐量总大小各300个。
1.3接口层
接口层主要提供被动采集出来的网络状态,目前包括GOOD,BAD,UNKNOWN,OFFLINE。
1)GOOD:3G和广义的4G,任一条件满足标记为GOOD状态。通过阈值标记3G和广义的4G,httprtt大于等于273ms,tcprtt大于等于204ms,即标记为3G状态。小于这两个值被标记为广义的4G,所谓广义的4G包含4G、WiFi、以及质量较好的各种接入网络。
2)BAD:慢2G,2G和httprtt大于1.31秒,任一条件满足标记为BAD状态。通过阈值标记慢2G和2G,httprtt大于等于2.01秒,tcprtt大于等于1.87秒,标记为慢2G。httprtt大于等于1.42秒,tcprtt大于等于1.28秒,标记为2G。httprtt大于1.31秒,为百度App的Feed刷新业务阈值。
注:上面涉及的时间值为nqe内部的机制,具有普适性。
3)UNKNOWN:非法的httprtt,tcprtt,吞吐量,任一条件满足标记为UNKNOWN状态。何为非法?值为-1为非法,那什么条件被标记为-1呢?首先初始化时会被标记为-1,其次在从来没有获取到过httprtt,tcprtt,throughput的值时,会使用本地默认的值做为判断标准,这是一种容错处理。
4)OFFLINE:依赖平台能力进行判断,Android平台依赖ConnectivityManager获取NetworkInfo,通过NetworkInfo的isConnected获取是否连接,如果未连接则判断为OFFLINE状态,如果NetworkInfo为空则判断为OFFLINE状态。
六、弱网状态下百度App如何改善用户体验