作者 | 蔡锐
编辑 | Yonie本文是百度网络优化系列文案的终结篇,该文深入探讨了网络优化中最为繁杂且难验证和分析的问题——弱网问题。本文经授权转载自【百度App技术】。 前言
网络优化处理的核心问题有三个,第1是安全问题,咱们在系列《一》DNS 优化 进行了仔细的讲解。第二是速度问题,咱们系列《二》连接优化 亦做了仔细的介绍。第三是弱网问题,它是网络优化中最为繁杂且必须反复验证和分析的问题,咱们的系列《三》弱网优化便是要深入探讨这个问题。
关联链接:
《百度 App 网络深度优化系列(一):DNS 优化》:
https://www.infoq.cn/article/3QZ0o9Nmv*O0LoEPVRkN
《百度 App 网络深度优化系列(二):连接优化》:
https://www.infoq.cn/article/CDaih849Ao4rS_pctQ2T
背景 弱网优化必须处理的核心问题有两点:移动网络环境如此繁杂,咱们怎样确定当下便是弱网环境。确定为弱网环境下,咱们怎样提高弱网下的成功率,降低弱网下的时延,从而提高用户的网络体验。
百度 App 承载着亿级流量,弱网比例 0.95%,可谓不小,这个比例是怎样得来的呢?还是要从什么是判断弱网指标说起。
判断弱网的指标
首要咱们来探讨下都有那些指标会影响到网络的质量,包含 httprtt,tcprtt,throughput,signal strength,bandwidth-delay product。
影响网络质量的指标
1.httprtt httprtt(http Round-Trip Time)又名 TTFB(Time to first byte),指从客户端请求的第1个字节起始发送到接收到 http header 的第1个字节的时间差。httprtt 的时间倘若过长,一方面是客户端本身接入网络质量的问题,另一方面是服务的延时比很强。
2.tcprtt tcprtt(tcp Round-Trip Time)指客户端 tcp 信道第1个字节发送到接收第1个字节的时间差。由于 HTTP 协议底层是基于 TCP 的,因此在复用同一条 tcp 连接的前提下,httprtt 的时间是包括 tcprtt 的时间的。大部分状况下 httprtt 已然能够说明问题的原由。
3.throughput throughput,中文名字吞吐量,它是用来衡量单位时间内成功传送数据的数量,是能够比较客观的衡量网络质量的指标。吞吐量 =(获 bits 结束体积 - 获 bits 起始体积)/(获 bits 结束时间 - 获 bits 起始时间),这儿有个细节必须重视,posix socket 的 read 函数返回值是 bytes,因此要乘以 8 得到 bits。一般在 httprtt 比较小的状况下,网络依然很慢,这个时候就能够运用吞吐量来确定网络的质量。
4.signal strength signal strength,这儿指的是没线信号强度,在 Android 上能够经过 PhoneStateListener 的 onSignalStrengthsChanged 办法获取到信号强弱,但要重视只能在 Android M 以上的版本才生效。iOS 上暂时无可靠的实现。
5.bandwidth-delay product bandwidth-delay product,中文名 带宽时延乘积,指的是一个数据链路的能力(throughput)与来回通信延迟(rtt)的乘积。带宽时延乘积的结果是比特不是位,这个比特值反应出当前网络管道的最大容量。TCP 中有一个窗口体积的概念,会限制发送和接收数据的体积,因此 TCP 窗口体积的调节是直接受带宽时延乘积的影响,按照带宽时延乘积的值去设置套接字的 setsockopt 办法,设置的 option 是 SO_RCVBUF(接收缓冲区体积)和 SO_SNDBUF(发送缓冲区体积)。
经过上面的内容,咱们对影响网络质量的指标有了必定认识,针对区别的制品,影响网络质量的指标能够理解成同样的,但针对每一个指标的阈值肯定是不同样的,由于这包括着业务场景,例如抖音是视频类网络传输,微X是长连接数据传输,百度是文本照片类数据传输。还包含服务端配备,区别制品线的服务集群能力肯定不同样,例如返回客户端的服务端耗时肯定不同样。因此针对区别的制品弱网指标是基本一致的,然则指标的取值肯定是不同样的。
怎样创立弱网标准
创立弱网标准是一个循序渐进的过程,在一穷二白的时候咱们应该怎样创立这个标准呢?答案分为三个周期。
创立弱网标准的过程:
第1周期,线下进行测试。获取有些符合咱们预期的阈值,这个时候咱们必须借助有些网络测试工具,例如苹果的 Network Link Conditioner,Facebook 的 ATC(Augmented Traffic Control),来获取到线下区别网络状况的阈值,通常咱们会测试 App 冷起步的场景,网络切换的场景,DNS 故障场景,弱网场景(通常都是配置上下行的带宽,丢包率,延迟,DNS 延迟参数,或更为简单的是运用工具默认的有些弱网配置)。 第二周期,线上进行验证。经过线下充分测试获取到的阈值,在线上能够获取到弱网的比例,在这儿百度 App 是针对特定场景的,例如 Feed 刷新,搜索落地页打开等,就算是在移动时代被大众公认的网络体验好的微X,亦只是在信令传输(收发信息)上做到极致优化,因此针对场景搜集弱网数据很要紧。第三周期,线上的反复实验。想做到理想的弱网效果,少不了线上反复的阈值调节,经过调节阈值比较针对场景的网络请求的成功率、耗时、连接复用率等指标,使咱们得到趋向于针对场景的恰当阈值。
聊了这么多,那样弱网的探测怎样实现呢?
网络探测的整体架构和实现
网络探测是弱网检测的基本,是不是能即时,正确的检测出网络质量,是咱们首要要处理的问题。咱们把网络探测划分为两部分,主动网络探测 和 被动网络采集。
1. 主动网络探测
所说主动探测,便是在触发了某些要求后,主动的进行网络探测,并根据必定的要求检测出是不是是弱网状态。百度 App 自研了主动探测组件,如下图所示。
主动网络探测
策略层 探测策略层经过多种策略的组合,使主动探测的即时性和准确性得以大大加强,咱们结合上面的策略层图来解释下检测维度的道理。
咱们分别在网络请求成功和失败的时候触发了弱网检测的规律。重点分为如下三种规律。
1)成功时,怎样判断进入弱网状态?检测 weakhttprtt 的阈值,这个值取决于业务的设置(通常这个值会针对特殊场景的请求取 95 分位或更大分位的值),大于这个值就会进入弱网检测,为了防止频繁触发探测加了时间间隔维度,日前定义的是 10 分钟。从线下模拟测试来看,只要大于这个阈值,检测结果必然是弱网状态。
2)成功时,怎样判断退出弱网状态?检测 goodhttprtt 的阈值,这个值取决于业务的设置(通常这个值会取整体网络的 95 分位或更大分位的值),少于这个值证明要切换回正常网络状态,为了防止频繁触发探测加了时间间隔维度,日前定义的是 30 秒。从线下模拟测试来看,只要少于这个阈值,检测结果必然是正常状态。倘若大于或等于这个阈值,亦不可证明必定不是正常网络,因此亦必须发起网络探测,然则因为这是在成功回调里,频次会很高,因此咱们加上时间间隔的限制 30 秒,还加入了次数的限制,连续成功次数 % 次数阈值(4 次)等于 0。但这看起来还是频次有点高,因此咱们引入了阶梯递增机制,随着次数的增长,成 60 秒几何倍数增长。
3)失败时,怎样判断进入弱网状态?首要会判断连续失败次数,连续失败次数 / 次数阈值(2 次)等于 1 并且连续失败次数 % 次数阈值(2 次)等于 0,相比成功,失败的次数检测较为苛刻,重点还是思虑多次触发网络检测损耗性能。
进入弱网状态后,就会触发基本能力层的 ping 和 dns query 的探测。
基本能力层 探测基本能力层,重点供给弱网检测的手段,一是 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。
判断出弱网状态后,会将结果供给给接口层。
接口层 接口层重点供给主动探测出来的网络状态,日前包含 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。时间的获取在吞吐量管理模块里完成,下面会讲到。获取机会在请求完成和请求销毁时。
如下为被动网络采集的整体架构图。
被动网络采集
能力层 能力层内容上面咱们已然讲过,重点采集 tcprtt、httprtt、throughput 三个维度的数据。
策略层 被动采集策略层经过多种策略的组合,降低各样采集数据的上报机会,降低性能的影响。
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 个。
接口层 接口层重点供给被动采集出来的网络状态,日前包含 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怎样改善用户体验
百度 App 在弱网下的手段
QUIC 在百度App弱网下的最佳实践
QUIC(Quick UDP Internet Connections)是新一代的互联网传输协议,最早源于 Google,它的详尽内容可参考资料【3】,本章咱们不做 QUIC 的科普介绍。
百度 App 的普通网络请求在弱网状态下会切换到 QUIC,本章重点讲解下百度 App 针对弱网下开启 QUIC 后遇到的问题,一是开启 QUIC 一旦遇到问题是不是能够回滚?二是在弱网下怎样能让流量尽可能的走 QUIC?针对这两个问题,咱们的处理方法是 QUIC 升降级原理和 QUIC 预连接。 QUIC 升降级原理
QUIC 升降级原理
如上图所示,QUIC 的升降级依赖于 HTTP Alternative Services,HTTP Alternative Services 是与 HTTP 相关的一个协议,它不是为 QUIC 专门设计的,在 HTTP 协议上重点负责新服务的替换,针对 HTTP1.1 协议,它是经过 HTTP 响应头传输回来的,因此只能在第二次请求时生效,如下面格式。
Alt-Svc: quic="alt.example.com:443", quic=":443"; ma=2592000
如上信息显示切换到 quic 协议,指定了域名服务和端口,并且指定了生效时间,以秒为单位。
Alt-Svc: clear
如上信息显示将 alter 配置清空
在网络库内部有一个 alter 连接和原连接的竞争机制,倘若 alter 信息已然存在,优先发送 alter 连接,而原连接会延迟发送,延迟时间默认 300ms,谁先成功就运用哪个连接,倘若 alter 连接在 QUIC 握手时失败,会记录这个 alter 信息的失败次数,并按照失败的次数,计算出一个过期时间,这个过期时间会随失败次数指数增多,最长为 2 天。当过期时间到期后,会清除这个 alter 信息,当这个 alter 连接在 QUIC 握手成功后,会清除这个 alter 信息。 QUIC 预连接 所说 QUIC 的预连接,便是在进入弱网状态前提前创立 QUIC 连接。大众都晓得 QUIC 引以为傲的 0RTT,但第1次创立连接的时候是必须 1RTT 的,客户端首要会向服务器发送一个 client hello 信息,服务器会回复一个 server reject 信息,这个信息中包含了 server config,有了 server config 后客户端就能够直接计算出密钥,完成 0RTT,详尽内容可参考资料【4】。
经过上面的原理,客户端拉取 server config 的成功概率会直接影响 QUIC 在弱网下的流量,因此咱们在 App 起步的过程中会做一次 QUIC 预连接,将 server config 拉取下来,这般等进入弱网后 alter 连接会大概率的竞争过原连接,从而走 QUIC 协议。
2. 复合连接在百度 App 弱网下的最佳实践
复合连接的详细原理能够查看《百度 App 网络深度优化系列《二》连接优化》里的详细介绍,百度 App 日前在弱网下只是针对照片网络请求开启了复合连接,由于照片请求不管是 HTTPDNS 结果还是 localDNS 的结果都是多个 IP,这是满足复合连接的前提。在弱网下多 IP 的尝试会比单 IP 的结果好些,另一弱网的比例相对较小,复合连接针对服务器的负载亦会小些。
百度 App 网络整体架构
百度 App 网络整体架构
百度 App 网络整体架构,以网络门面为中间层,隔离上层的最佳实践和底层的基本网络库。
最佳实践 客户端网络库的一部分工作量是在怎样让最佳实践做的更好,在音视频上,不管是 iOS 的 AVPlayer,还是双端的 ijkPlayer,都是运用 HTTPDNS 组件接管 DNS 模块,无所有接管网络模块。ReactNative 的网络模块 RCTNetworking,照片库 Android 的 Fresco 和 iOS 的 SDWebImage,WebView 组件 Android 的 Chromium 和 iOS 的 WKWebView,以及百度 App 的自有业务,都是经过网络门面的接口层直接接管。而针对第三方业务考虑到与宿主耦合的关系,直接运用 Android 的 HttpURLConnection 和 iOS 的 URLSession 系统标准的接口。
网络门面 网络门面重点包含,拦截器模块(供给给业务订制网络门面的机制)、并发队列模块(供给高中低以及非常高的网络请求优先级)、网络探测组件(弱网主动探测能力)、网络诊断模块(包含 https,ping,dns 的校验)、HTTPDNS 组件(《百度 App 网络深度优化系列《一》DNS 优化》里仔细讲解)、网络监控模块(客户端的打点机制,服务端的例行和突发监控)、HttpURLConnection 封装层、URLSession 封装层。
基本网络库 基本网络库包括两部分,一部分是基于 cronet 二次订制的统一网络库,一部分是 WebSocket 基本库(Android 的 JavaWebSocket,iOS 的 SocketRocket)。统一网络库内部包括连接优化的内容(在《百度 App 网络深度优化系列《二》连接优化》里仔细讲解),弱网优化的内容(上面说到的被动采集)。经过 AOP 的方式将底层协议栈注入进 HttpURLConnection(利用 URLStreamHandlerFactory)和 URLSession(利用 URLSessionConfiguration 的 protocolClasses 属性),两者都是系统供给的 URL Loading 机制。
收益
弱网优化的收益咱们重点从上面讲到的进入弱网状态后的手段来看,包含开启 QUIC,QUIC 预连接,开启复合连接。
1)弱网下开启 QUIC 后,网络连接成功率提高 0.01%,平均耗时降低 23.5%。
2)弱网下开启 QUIC 预连接后,QUIC 协议的 pv 从 37 万涨到 90 万。
3)弱网下开启复合连接后,bad 状态下耗时降低 2.5%,offline 状态下耗时降低 7.7%。
结语
系列一到系列三的内容到今天所有完成,期盼能对大众的工作和学习有所帮忙,感谢大众的连续关注和鼓励。生命不息,优化不止,做技术咱们是认真的。
作者介绍 蔡锐,9 年移动端研发经验,在百度先后主导过订制 ROM 行业、多屏互动行业、Hybrid 跨平台行业等多个技术行业的研发,日前担任百度 App 的客户端资深工程师,参与基本技术的科研,专攻动态化、性能和网络优化方向。
参考资料 https://chromium.googlesource.com/chromium/src/+/HEAD/docs/android_build_instructions.md
https://chromium.googlesource.com/chromium/src/+/HEAD/docs/ios/build_instructions.md
https://www.wolfcstech.com/2019/03/27/quic_2019_03_27/
https://www.wolfcstech.com/2017/03/09/QUIC%E5%8A%A0%E5%AF%86%E5%8D%8F%E8%AE%AE/
https://tools.ietf.org/html/rfc1918
https://github.com/Tencent/mars
|