一、网络优化维度
1、网络优化分析
基础网络的效率就像一辆列车,时延是火车的速度 (启动时间),而带宽就像火车的车厢装载量,整个传输的物理链路就像火车的铁轨。从网络的通信过程来看,共涉及到 三个模块:
- 1)、网络库 SDK 内部的设计与策略:I/O 并发模型,针对网络问题的优化。
- 2)、服务器性能:并发、带宽能力。
- 3)、网络相关:用户网络(弱网/强网)、运营商、网络链路等。
而对于网络的优化,我们可以从以下五个维度来进行。
1)、流量优化
精确获取网络流量的消耗量,解决整体均值掩盖单点异常流量的问题。
2)、网络监控
建设全面的网络监控,因为粗粒度的监控不能够帮助我们发现和解决问题。
3)、流量消耗
1、精准获取一段时间的流量消耗、网络类型、前后台。
2、用户流量消耗均值、异常率(消耗多、次数多)。
3、完整链路全监控(Request、Response)、主动上报。
4)、网络请求质量1、请求时长、业务成功率、失败率、TOP 失败接口,导致请求失败的原因通常有两种情况:
1)、弱信号:可以简单看成手机信号只有一两格的时候,这是不仅仅是信令(无线网络通信的都是一个个的信令)发出去困难,还可能导致不断切换网络、基站。App 只能在应用层做重试,因为弱信号一般都是一时的。
2)、拥塞网络:可以类比为堵车、排队的场景,数据包排队,信令也在排队。这时 App 不断重试,只会使得拥塞网络更为严重。我们只能让自己的非核心业务不要去排队,并让核心业务的数据量更少,协议来回更少。2、用户体验
3、请求速度、成功率:网络正常时如何更好地利用带宽提升网络请求速度?
4、弱网:网络不稳定是如何最大程度上保证网络的连通性?
5、安全:如何防止被第三方劫持、窃听甚至篡改?
5)、其它1、公司成本
2、带宽、服务器数量、CDN
3、耗电
2、网络优化误区
- 1)、仅仅关注流量消耗,忽视其它维度。
- 2)、仅仅关注均值、整体、忽视个体。
二、网络优化工具
1、Network Profiler
特点
- 1)、显示实时网络活动:发送、接收数据及连接数。
- 2)、需启动高级分析。
- 3)、仅支持 HttpURLConnection 与 OkHttp
打开高级分析
Run => Edit Cofigurations => 界面最右边 Profiling => 打开 Enable advanced profiling (required for API level < 26 only)
使用 Network Profiler 调试 WanAndroid 网络请求
Connection View
选中目标网络请求,可以看到在下方的 Connection View 一栏看到对应的网络数据,如下所示:
Size
Type
Status
Time
Timeline
选中 Connection View 特定的一条数据即可在右边看到该请求对应的网络数据。
Overview
该网络请求的预览信息
Response
Response Header 与 Body 信息
Request
Request Header 与 Body 信息
CallStack
网络请求的调用堆栈信息, 下图就是 Awesome-WanAndroid 发起一个网络请求所经历的调用堆栈:
2、Charles
特点
- 1)、断点功能
- 2)、Map Local
- 3)、弱网环境模拟
使用断点功能
1)、右键点击要断点的 URL,选中 BreakPoints 开启断点功能。
2)、点击顶部 Proxy => Breadkpoint Settings。
3)、双击 Breakpoints Settings 面板中的目标
URL,在弹出的 Edit Breakpoint 面板中进行编辑。
4)、这里默认选择断点 Request 与 Response,我们可以选择仅断点 Response 或 Request。点击确认即断点设置完成。
5)、然后,我们就可以点击主面板右侧的 Edit Response 编辑 Response,修改完成后点击最下方的 Execute 即可。
使用 Map Local
1)、自由模拟服务端的返回数据,以提前进行接口测试。
1)、右键点击要使用 Map Local 的 URL,选中Map Local 开启断点功能。
1)、然后,我们在 Edit Mapping 面板中选择 Map To 的 Local path,选择本地设定的 maplocal 本地数据(例如 JsonString)
弱网模拟功能
- 1)、注意开启前需将 Map Local 关闭。
- 2)、点击 Proxy => Throttle Setting => 选中 Enable Throttling
- 3)、这里预设了很多模拟设置,我们只需将 网络包传输的速率 Throttle preset 设置为较低的速率(一般设为 256/512)。
3、Wireshark
WireShark 主要可以用来对四种流进行跟踪,如下所示:
TCP
UDP
SSL
HTTP
4、TcpDump(网络数据包嗅探器)
5、Stetho
- 1)、在 build.gradle 中,除了 Stetho 依赖外,还需添加 'com.facebook.stetho:stetho-okhttp3:1.5.0'。
- 2)、在 Application 的 onCreate 方法中初始化 'Stetho.initializeWithDefaults(this)'。
- 3)、调用 OkHttp 的 'addNeworkInterceptor' 方法添加 Stetho 用于收集网络信息而提供的网络拦截器。
- 4)、访问 Chrome 调试页面 'chrome://inspect'。
6、其它的性能检测工具
- strace:跟踪 Socket 相关的系统调用。
- netstat:记录多种网络栈和接口统计信息。
- ifconfig:记录接口配置。
- ip:记录网络接口统计信息。
- ping:测试网络连通性。
- traceroute:测试网络路由。
- /proc/net 命令:查看网络统计信息,Android TrafficState 使用了 /proc/net/xt_qtaguid/stats 和 /proc/net/xt_qtaguid/iface_stat_fmt 文件来统计 App 的流量信息。
三、精准获取流量消耗
1、如何判断 App 流量消耗偏高?
- 1)、绝对值看不出高低。
- 2)、对比竞品,相同 Case 对比流浪消耗。
- 3)、异常监控超过正常指标。
2、测试方案
- 1)、打开手机设置 => 流量管理 => 仅允许目标 App 联网
- 2)、可以查找出大多数的问题,但是线上场景线下可能遇不到。
3、线上流量获取方案
1)、TrafficStats
特点
- API 18 以上。
- 记录手机重启以来的数据流量。
API - getMobileRxBytes():通过蜂窝流量接收到的信息。
- getUidRxBytes(int uid):获取指定 uid 的接收流量。
- getTotalRxBytes():总发送流量。
缺点
无法获取某个时间段内的流量消耗。
2)、NetworkStatsManager
API 23 之后。
特点
- 1)、获取指定时间间隔内的流量信息。
- 2)、获取不同网络类型下的消耗。
NetUtils.getStats
获取指定时间间隔的 蜂窝 + WIFI 流量总信息
4、前后台流量获取方案
问题:线上反馈 App 后天流量消耗大?
只获取一个时间段的流量不够全面。
实现原理
后台定时任务 => 获取时间间隔内流量 => 记录前后台 => 分别计算 => 上报 APM 后台 => 流量治理依据
小结
- 1)、该方案无法获取应用在前后台切换时的流量,因此有一定的误差,但这个误差是可以接受的。
- 2)、结合精细化的流量异常报警针对性的解决后台跑流量的问题。
四、网络请求流量优化
1、常见使用网络的场景
1)、数据压缩
POST 请求 Body 使用 GZip 压缩,同时服务端返回 Body 也使用 GZip 压缩。
2)、图片
- 图片上传前压缩。
- 图片使用策略细化:让 服务端/CDN 云服务器 优先使用缩略图/WebP格式图片。
3)、性能日志上报:批量 + 特定场景上报
APM 相关、单点问题相关。例如埋点数据可以等到某一时机点(例如 开启了 WIFI、数据量过大必须上传一部分时)再上传。
4)、数据缓存
服务端返回加上过期时间,避免每次重新获取。 节约流量且大幅提高数据访问速度,更好的用户体验。
Request 缓存设置 - 1、Pragma:no-cache:去服务器拉取最新的资源,不使用缓存。
- 2、If-Modified-Since:datetime:如果资源在客户端提供的时间后发生改变,服务器会返回新的资源,否则使用缓存。
- 3、If-None-Match:etagvalue:如果资源的标识和服务器的不同,返回新的资源。
当 Request 的头部是 2 和 3 时,如果服务器的资源没有修改,则服务器会返回 HTTP/304 Not Modified,客户端会使用缓存的 Response。
Response 缓存设置
HTTP Response 是否可以缓存是由 Response 的头部控制的,服务器可以通过 Expires 和 Cache-Control 控制 Response 如何在客户端缓存。
Expires
Expires 头部会包含一个日期,即该资源缓存的有效期,客户端有新的相同请求时,如果资源缓存没有过期,则使用缓存资源,服务器不会返回任何东西。
Cache-Control
Cache-Control 可以标明 Response 如何存储及其如何使用,其选项如下所示: - 1)、public:Response 可以存储在任何 Cache 中,包括共享的 Cache。
- 2)、private:Response 存储在私有 Cache 中,只能被一个用户使用。
- 3)、no-cache:Response 将来不会被使用。
- 4)、no-store:Response 将来不会被使用,也不会写到磁盘上。
- 5)、max-age=#seconds:Response 在设定的时间内可以被重复使用。
- 6)、must-revalidate:和原始服务器确认 Response 是最新后,可以使用缓存。
OKHttp 无网数据缓存实现
POST 在 OKHttp 中默认不会缓存,因为 POST 一般是用来修改数据的。在 Awesome-WanAndroid 中的 HttpModule—cacheInterceptor 中就已经实现了 OKHttp 的无网数据缓存,代码如下所示:
File cacheFile = new File(Constants.PATH_CACHE);
Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
Interceptor cacheInterceptor = chain -> {
Request request = chain.request();
if (!CommonUtils.isNetworkConnected()) {
// 无网时强制使用数据缓存,以提升用户体验。
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (CommonUtils.isNetworkConnected()) {
int maxAge = 0;
// 有网络时, 不缓存, 最大保存时长为0
response.newBuilder()
.header("Cache-Control", "public, max-age=" + maxAge)
.removeHeader("Pragma")
.build();
} else {
// 无网络时,设置超时为4周
int maxStale = 60 * 60 * 24 * 28;
response.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.removeHeader("Pragma")
.build();
}
return response;
};
// 缓存优化
builder.addNetworkInterceptor(cacheInterceptor);
builder.addInterceptor(cacheInterceptor);
builder.cache(cache);
5)、离线包、增量数据更新
加上版本的概念,仅传输有变化的数据。
6)、请求头压缩
如果请求头不变,服务端可以使用映射缓存 请求头 MD5 : 请求头,之后请求头都使用 MD5 即可。
7)、优化发送频率和时机
8)、合并网络请求、减少请求次数。
9)、流量兜底能力
如果发现流量异常,我们可以通过后台服务器终止协议交互,以避免问题恶化。
2、流量统计
我们可以利用 network-connection-class 进行流量统计,它内部使用的是 API 8 的 TrafficStats 类,用于获取整个手机或者某个 UID 从开机算起的网络流量。
1)、四个核心 API
// 从开机开始Mobile网络接收的字节总数,不包括Wifi
getMobileRxBytes()
// 从开机开始所有网络接收的字节总数,包括Wifi
getTotalRxBytes()
// 从开机开始Mobile网络发送的字节总数,不包括Wifi
getMobileTxBytes()
// 从开机开始所有网络发送的字节总数,包括Wifi
getTotalTxBytes()
2)、对应的Linux 内核 proc 统计接口
// stats接口提供各个uid在各个网络接口(wlan0, ppp0等)的流量信息
/proc/net/xt_qtaguid/stats
// iface_stat_fmt接口提供各个接口的汇总流量信息
proc/net/xt_qtaguid/iface_stat_fmt
3)、工作原理
- 1)、读取 proc,并将目标 UID 下面所有网络接口的流量相加。
- 2)、Android 7.0 之后只能通过 TrafficStats 拿到自己应用的流量信息。