网络优化
优化维度
- 流量维度
- 一段时间流量消耗的精准度,网络类型、前台还是后台
- 监控线上用户流量消耗均值、异常率(流量消耗过度、请求次数过多、下载的文件过大)
- 理想情况下可以完整链路监控(
Request
、Response
),主动上报
- 质量维度
- 用户体验:请求速度、成功率
- 监控相关:请求时长、业务成功率、失败率、Top失败接口
工具选择
Network Profiler
- 抓包工具:断点功能、
Map Local
、弱网环境模拟Charles
:Java 开发跨平台,mac 使用比较多Fiddler
:Windows 上面使用比较多Wireshark
TcpDump
Stetho
精准获取流量消耗
如何判断流量偏高
- 绝对值不看高低
- 与竞品对比,相同功能的情况下对比流量消耗
- 后台监控到流量超过正常指标
流量测试方案
线下流量测试方案
- 手机设置只有目标应用可以联网
- 通过抓包工具限制只允许本APP联网。【注:不能以域名为限定基准,因为有的三方SDK、网页等你并不清楚它们的域名,所有会把本应放行的域名给拦住了】
- 上述两种方式可以解决大多数问题,但是线上场景可能抓不全,如有的H5 zip包并不一定是随app版本一起发布
线上流量消耗方案
通过
TrafficStats
(API8)获取流量:【缺点】无法获取某个时间段的流量消耗
1
2
3
4
5
6TrafficStats.getUidRxBytes() // 指定Uid所有网络接收的流量
TrafficStats.getUidTxBytes() // 指定Uid所有网络发送的流量
TrafficStats.getMobileRxBytes() // 获取手机移动网络接收的流量
TrafficStats.getMobileTxBytes() // 获取手机移动网络发送的流量
TrafficStats.getTotalRxBytes() // 获取手机所有网络接收的流量
TrafficStats.getTotalTxBytes() // 获取手机所有网络发送的流量通过
NetworkStatsManager
(API23)获取流量统计【推荐】可以获取指定时间间隔内的流量信息和不同网络类型下的消耗
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34public static long getNetData(Context context, long startTime, long endTime) {
long netDataRx = 0;
long netDataTx = 0;
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
NetworkStatsManager networkStatsManager = (NetworkStatsManager) context.getSystemService(Context.NETWORK_STATS_SERVICE);
NetworkStats networkStats = null;
try {
networkStats = networkStatsManager.querySummary(ConnectivityManager.TYPE_MOBILE, telephonyManager.getSubscriberId(), startTime, endTime);
} catch (RemoteException e) {
e.printStackTrace();
}
if (networkStats == null) return 0;
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
int uid = getUidByPackageName(context);
while (networkStats.hasNextBucket()) {
networkStats.getNextBucket(bucket);
if (uid == bucket.getUid()) {
netDataRx += bucket.getRxBytes();
netDataTx += bucket.getTxBytes();
}
}
return netDataRx + netDataTx;
}
public static int getUidByPackageName(Context context) {
try {
PackageManager pm = context.getPackageManager();
ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
return ai.uid;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return -1;
}
前后台流量获取方案
后台定时任务 -> 获取间隔内流量消耗 -> 区分此时的前后台状态保存数据 -> 合适的时机将数据上报
【注:该方案有一定的误差,但是该误差处于可接受范围】
网络请求流量优化
流量消耗点
- 数据:API、资源包(升级包、H5、RN、flutter)、配置信息
- 图片:上传、下载
- 监控:APM相关、单点问题
优化点
- 服务端返回数据加上过期时间,客户端网络框架做好缓存逻辑,节约流量且能大幅提高数据访问速度
- 数据增量更新,给数据加上版本的概念,只传输有变化的数据,例如配置信息、省市区县等并不会经常变化的数据
- 请求响应数据压缩,对Post请求Body使用
Gzip
压缩,如需最求更高压缩率可以使用brotli
- 请求头压缩,对一些共性请求头可以在服务器中做好MD5的缓存,客户端只需传递该MD5即可,无需每个请求都携带完整的请求头
- 图片视频压缩,在无需保存源文件的场景下可以在上传之前本地先行压缩数据
- 优化网络发送频率和时机,相应打点信息性能日志等需要合并网络请求减少请求次数,缓存一定量值或
wifi
下才上报 - 条件允许的情况下优先显示缩略图和更优编码的格式图片,需要服务端提供多分辨率图片和格式转码的支持
网络请求质量优化
质量指标
- 网络请求成功率
- 网络响应速度
请求过程
- 请求到达运营商
Dns
服务器并解析成对应的 IP 地址 - 创建连接,根据 IP 地址找到相应的服务器,发起一个请求
- 服务器找到对应的资源原路返回访问的用户
【优化点】DNS 相关
- 问题:DNS被劫持、DNS解析慢
- 方案:使用
HttpDNS
, 绕过运营上域名解析的过程,不使用传统的DNS协议向53端口发送请求,而是使用HTTP协议向DNS服务器的80端口发送请求 - 优势:可以绕过DNS劫持的风险、降低平均访问时长、提高连接成功率
【优化点】协议版本升级
- 1.0:版本TCP连接不复用
- 1.1:引入持久连接,但数据通信还是按顺序进行
- 2.0:多工,客户端、服务器双向实时通信
网络请求质量监控
之所以客户端也需要做网络监控,是因为有的网络请求并没有到服务器就失败了,服务器没法统计
- 在相应的网络请求库中增加接口请求耗时、成功率、错误码监听统计
- 在图片请求库中增加图片加载的每一步耗时统计
网络容灾机制
- 备用服务器分流
- 多次失败后一定时间内不再进行请求,避免雪崩效应【注意:时间间隔各个用户应该是随机的,避免间隔一段时间后还是出现大批量的请求】
其它
- CND加速、提高带宽、动静态资源分离(更新后清理缓存)
Okhttp
默认单个域名同时请求数量位5个,这是为了防止某个域名请求过多,其它域名的请求没法执行,如果做了域名收敛,只有一个域名,那么可以适当增加单个域名最大同时请求数量