请选择 进入手机版 | 继续访问电脑版

默认
打赏 发表评论 4
腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(上篇)

导读:在移动应用开发中,应用上线了只是一个开始,噩梦在后面:手机越用越卡为哪般?手机发烫是为何?谁偷走了用户的钱包?如何瘦成一道闪电?这些问题解决起来都是非常麻烦的,腾讯移动品质中心(TMQ)成立了专项测试团队来解决这些问题。


1、前言


最近几年,我们针对 Apps 的网络性能优化工作并不多,但每一次都是投入人手较多、时间跨度较长的大任务。本文将给读者们一个一年多以前为公司的某产品成功优化网络流量的案例。速度、成功率与流量正好是 Apps 网络优化的几大重点,希望本文我们分享的思路能够给诸位正在开展或将来会开展此类工作的读者们一些启发。

在上文《腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率》中,我们重点讲解了提升上传速度和成功率的“鱼翅”项目,分析了在移动网络下影响上传速度和成功率的因素,一次次的调优算法并验证,最终提炼出了能应对网络质量瞬息万变的鱼翅算法。而本文则是讲解了我们在不删减功能的前提下是如何提升某产品待机流量的,重点讲解了流量测试的基本方法,流量自动化测试的经验以及提炼出的流量优化的通用方法。

目前,国内几大运营商的移动数据业务还都处于按流量计费状态,且超出套餐外的流量收费较为昂贵。 那么,一款 APP 是否消耗过多的流量,在用户体验方面影响就显得比较大。

一年多前,部门内的某安卓版的产品收到用户投诉,从安卓系统的流量统计中查看到该产品消耗的背景流量偏高,背景流量即 APP 在用户无操作时后台运行消耗的流量,主要用于一些推荐和更新等信息的推送,这部分流量对用户是有意义的,但是如果消耗过多而用户没有感知到足够信息的推送,在用户看来就等于是变相的“偷”走了用户的金钱。

我们经过自测,该产品在常驻后台运行时 24 小时消耗流量 600KB 左右,而竞品流量消耗在 150KB 左右,一个月下来也是一笔不小的开销,我们团队对该产品的背景流量进行了认真的分析和优化,经历了 4 个月左右的努力,成功的将 24 小时背景流量降到了 100KB 以下,并且基本功能无删减。优化后的版本上线后为用户节省了大量的流量,也就是替用户省了钱,收到用户的一致好评。

整个流量优化阶段现在回头想来,经历过三个大的阶段:

  • 首先:我们花了大量的精力研究如何测试流量消耗,如何精确的得到每个功能点消耗了多少流量,因为如果我们不了解现状,根本无法去优化流量;
  • 其次:我们针对每个功能点,根据其功能逻辑,探讨优化方法,以及从全局来看,这些功能点有无精简的可能,从项目之初的无任何优化经验,到项目结束时总结和固化了众多的流量优化经验,我们成功的将流量降到了理想范围;
  • 最后:我们思考如何将本次优化的成果持续保持下去,即后续的新增特性不能恶化流量消耗,我们开发并完善了流量自动化监控系统,有力的保障了后续的版本流量不恶化。

下面我们就按照项目的三个阶段来详细的分享优化过程中积累的经验和方法。因文章内容较多,本次分享将分为《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(上篇)》和《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)》两篇供开发者同行来阅读,我们首先来开始本文(即“上篇”)的内容。

另外,强烈建议您同时阅读:《现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障》《移动端IM开发者必读(一):通俗易懂,理解移动网络的“弱”和“慢”》、《移动端IM开发者必读(二):史上最全移动弱网络优化方法总结》。

2、系列文章


本文是系列文章中的第2篇,总目录如下:


3、本文内容概述


项目之初,我们对该产品的流量消耗情况进行了详细的摸底,我们要搞清楚,我们的流量到底消耗在哪了?有没有多余和浪费?这就是本文将来展开的主题:即摸清现状

4、流量测试方法


首先我们对该产品 24 小时的消耗的背景流量进行测试。那么流量测试都有什么方法呢?首先得搞清楚流量是什么?我们的手机通过运营商的网络访问 Internet,运营商替我们的手机转发数据报文,数据报文的总大小(字节数)即流量,这里的数据报文包含手机上下行的报文。由于数据报文采用 IP 协议传输,运营商计算的流量一般都是包含 IP 头的数据报文大小。

搞清楚了流量的定义之后,那么我们可以思考如何来获取应用消耗的流量。最直接的办法就是在手机上抓包,分析报文的总大小,即为应用消耗的流量;如果手机没有 root,不方便抓包时,可以设置一个代理服务器,手机通过 WiFi,设置为代理服务器方式访问 Internet,在设置的代理服务器上抓包进行流量分析。另外,安卓系统目前也提供了 TCP 流量的统计,如果被测应用使用的是 TCP 协议,则可以直接取该统计值来计算流量。

间接的流量测试方法比如通过第三方流量监控软件来获取流量,还有通过运营商的流量查询方法(短信,营业厅等方法)来获取流量的消耗情况,也都可以达到我们获取流量的目的。

大家在流量测试的过程中,需要根据自身应用的特点,因地制宜的选择最合适方便的测试方法。下面我们详细的介绍两种最常用的流量测试方法:抓包测试法、统计测试法。

4.1抓包测试法


测量流量最直接的方法就是抓包。在 APP 运行期间,把手机收发的所有报文都抓取下来,再计算收发报文总大小,即 APP 消耗的流量。但是如果我们需要测试某一个 APP 消耗的流量呢?项目之初,我们想到的方法是通过第三方应用,来禁用其他 APP 的联网权限。下面详细介绍一下这种方法的操作步骤:

【第一步】:限制其他 APP 联网权限

手机上很多 APP 的进程是常驻后台的,即使不运行,也会有网络报文。所以,为了准确的抓取被测应用的报文,需禁止其他应用的联网权限。我们可以通过手机管家类的软件来禁止联网。如图 5-3 所示,为腾讯手机管家对应用联网控制的界面。

1.png
▲ 手机管家禁止联网示意

【第二步】:手机上抓包

安卓系统上常用的抓包工具是 tcpdump,具体的操作步骤如下:

  • PC 上安装 adb,直接下载或者通过 eclipse 中的安卓开发环境自带的工具集获得。
  • 下载 tcpdump: http://www.strazzere.com/Android/tcpdump
  • 检查设备连接情况:
    2.png
  • 把 tcpdump 拷贝至 /data/local 目录,注意,/data/local 目录需要 root 权限才能拷入,所以先使用 adb push 拷贝至手机 /sdcard 目录,再使用 adb shell 进入命令行,使用 su 进入 root 状态, cp 至 /data/local 目录:
    3.png
  • 为 tcpdump 添加可执行权限:
    4.png
  • 启动抓包,使用命令 /data/local/tcpdump -p -vv -s 0 -w /sdcard/capture.pcap
    5.png
    “ Got”后面的数字表示当前抓到的包的数量。如果在变化,表示有网络流量。
  • 我们刚刚把抓包的结果保存在了 /sdcard 目录下,导出抓包的结果到电脑。

大家看了这么多步骤是不是觉得很复杂,不过不要紧,我们自研的 GT 工具已经把 tcpdump 抓包功能集成进去了(腾讯GT开源工程链接),后面介绍 GT 的章节里面会详细介绍抓包方法,在手机上有用户操作界面可以实现一键式抓包,另外 GT 也提供了命令行方式的接口启动抓包,启动命令为:
adb shell am broadcast – a com.tencent.wstt.gt.plugin.tcpdump.startTest – es filepath “/sdcard/GT/Tcpdump/Capture/test.pcap”

停止抓包命令为:
adb shell am broadcast -a com.tencent.wstt.gt.plugin.tcpdump.endTest

后面可以看到,命令行方式可以方便的做进自动化测试脚本中。

【第三步】:根据抓包文件统计流量

这里需要对抓包文件分析,获得抓取的报文总流量,目前 PC 上的抓包软件 wireshark 就提供这样的统计功能。用 wireshark 打开刚刚的抓包文件,点击 Statistics->Summary,如下图所示。

6.png
▲ wireshark 流量统计功能

流量的数值为 Bytes 一行的 Displayed 一栏。如下图所示。

7.png
▲ wireshark 流量统计详细页

4.2统计测试法


安卓系统自身提供了 TCP 收发长度的统计功能,一般 APP 和后台服务器之间的通信都是基于 TCP 的,所以我们可以利用此统计来测试我们 APP 的流量,而且安卓提供的该统计功能是按照 APP 纬度来统计,不需要禁止其他 APP 的联网权限。

下面简单的介绍一下操作步骤:

  • 使用 ps 命令查看所测 APP 的 uid,如下图举例,手机 QQ 的 UID 为 10000 + 155 = 10155(即红框数字 +10000)。
  • 进入 /proc/uid_stat/10155 目录, cat 获取当前 tcp_snd 和 tcp_rcv 的初始值:
    8.png
  • 进行测试一段时间后再次获取 tcp_snd 和 tcp_rcv 的值:
    9.png
  • 所测时间内的流量计算:
    发送流量: tcp_snd_new – tcp_snd_old = 418375 – 391582 = 26793 bytes
    接受流量: tcp_rcv_new – tcp_rcv_old = 5902341 – 5840747 = 61594 bytes


了解了流量测试方法,我们就开始着手测试我们产品的流量消耗情况。

5、每天 24 小时待机测试的加速


在开始测试流量时,我们首先需要搞清楚测试场景,我们需要测试产品的背景流量,在不操作 APP 的情况下,我们的测试时长是多少?为了研究这个问题,我们经过了详细的测试,发现一天中每个时间段的流量都是不一样的,即上午的一小时消耗的流量可能就与下午的一小时消耗的流量不一样。在研究了 APP 的运行机制后,我们发现, APP 后台运行时的流量一般都是按照时间策略触发的,每天的各个时间段的发包频率是不一样的,基于这种机制,我们就需要测试 24 小时 APP 的背景流量。

搞清楚了测试场景之后,摆在我们面前的难题来了,如果每轮测试都需要 24 小时,那我们的测试效率太低了,每次优化后的版本都需要测试 24 小时,我们可能一年也做不完这个项目。所以我们开始着手研究如何提升测试效率。

首先想到的是让系统的时间跑得快一些,比如说实际的一秒钟,我们让系统的时间变化一分钟,这样 24 小时不就变成 24 分钟了吗?我们赶紧验证我们的想法,我们自己写了一个 APP,周期性的改变系统时间,发现这种方法是凑效的,但是相比于正常 24 小时的流量,这种方法测试出来的流量是偏少的,通过日志打印,我们发现这种加速方案对大部分协议是生效的,但是对一部分协议不生效,我们对不生效的协议进行了代码分析,发现这部分协议的发送是采用了相对定时器的机制发送,相对定时器是基于系统 ticks 计数来进行任务调度的,修改系统时间对此不凑效。

我们开始想到的解决办法是在代码中重构相对定时器的函数,例如传入的参数是一小时调度一次,重构后的相对定时器首先会将传入参数除以 60,再传递给系统相对定时器函数,这种办法是凑效的,但是有一个缺点,就是必须单独编译一个版本,不能直接使用发布版本来测试,这也会对我们的测试效率产生影响,后来经过研究,我们想到了 hook 的方式,动态重构发布版本的相对定时器函数,达到了同样的功效。

最后我们将研究成果和方法总结概括一下:

  • 1) 基于时间点的定时任务采用周期性修改系统时间来加速:
    这种方式在代码实现时是调用了 (AlarmManager)am.set(AlarmManager.RTC_WAKEUP, point, sender) 方法,其中 point 为系统绝对时间,周期性的修改系统时间加速该类定时任务是有效的,这里我们使用 APP 的方式来实现,每隔一秒使系统时间增长一分钟。
  • 2) 相对定时器任务采用 hook 的方式重构定时器函数:
    相对定时器采用 Handler 的 sendMessageDelayed(msg, long) 方法进行定时调度,该方法是基于系统 ticks 计数来进行任务定时调度的,我们采用 hook 的方式,修改系统的 sendMessageDelayed 函数,将传入的时间除以 60,这样, 1 小时的周期定时器实际为 1 分钟。 Hook 采用 xposed 框架开发。

使用这两种加速方式, 24 分钟即可完成 24 小时的流量测试,大大提升了测试效率。

这种方法也有局限性,比如后台服务器下发的 push 等信息是后台服务器根据自身的时间策略下发的,终端的这种加速对后台是不起作用的,但是,由于 push 的流量占比不是很大,所以在我们的优化中这种影响是可以忽略的。

6、流量精细化监控


项目进行到这里我们已经能很快的获得 24 小时 APP 的背景流量了,但是这个流量数值是无法指导我们后续的优化工作的,因为我们不知道从哪个网络功能开始着手分析。所以我们需要搞清楚,我们这些流量消耗都干了什么事情。

首先,我们跟业务开发团队进行了沟通,跟开发人员了解流量现状,我们了解到,我们这款 APP 没有采用长连接,所以服务器上的更新,通知等信息是需要 APP 周期性的向服务器发送查询消息来获取的,不同业务的查询消息的发送频率是不一样的。由于业务开发团队是按照特性( feature)来划分的,各自为营,不同开发小组开发的消息结构和发送机制也都是不统一的,而且有很多消息的发送时机选择得也不是很好,服务器返回的内容很多都不是最精简的,存在冗余,所以导致消耗流量过多。

面对如此杂乱无章的流量消耗,我们需要搞清楚我们的 APP 每条 IP 报文的功能,都干了什么事情,理清楚了这些之后才能进行逻辑上的优化,进而进行流量的优化。下面会详细介绍我们是如何理清楚当前的流量消耗的,如何变无序为有序的。

6.1按照域名进行流量细分


首先,我们对抓包进行了详细的分析,由于我们的 APP 与后台通信只使用了 HTTP 协议,所以从抓包中是能分析出一些细节的,我们发现 HTTP 报文发往的 host,即域名,例如 www.tencent.com,是不尽相同的,域名是用来区分不同的后台服务器的,代表不同的功能,这种方式有助于不同的后台服务器处理不同功能的报文,这样后台服务器可以按照功能来独立部署和升级。基于这个发现,我们首先对报文按照域名来分类。

下面我们详细介绍一下如何按照域名来分别统计流量。首先使用 wireshark 自带的过滤功能只显示 HTTP 报文,在 filter 处输入 http 即可,如下图所示。

10.png
▲ 过滤 HTTP 报文

这样 wireshark 的视图总就只剩下 HTTP 报文了,我们点开一条报文,点开 HTTP 的内容,可以看到有 Host 字段,该字段表示该条 HTTP 报文通信的后台服务器的域名。那么如何统计抓包中共向多少后台服务器发送过请求呢,可以按照如下方法进行。

首先,在上面按照 HTTP 过滤条件过滤之后的基础上,点击 File->Export Packet Dissections -> as “ Plain Text” file,如下图所示。

11.png
▲ 报文导出为文本

该步骤可以将过滤后的报文的详细信息(包含 HTTP 头的信息)存储成文本。格式如下图所示。

12.png
▲ 报文文本格式

使用 Notepad++ 的搜索功能,搜索关键字“ Host:”,点击“在当前文件中查找”,在搜索结果框中会列出所有的结果,所有的 Host 一目了然。如下图所示。

12-2.png    13.png
▲ 报文文本中过滤域名

那么,对于某一个域名,可以使用条件 HTTP.host == "www.test.com" 将该域名的报文过滤出来,如下图所示,然后就可以得知该域名的 IP 地址。一般同一个域名可能有多个 IP 地址与之对应,因为目前后台服务器一般是一个集群,每个终端都会被负荷分担至某几个 IP 的服务器。如下图所示。

14.png
▲ 确定域名的 IP

然后使用 IP 地址过滤,以上图举例,过滤条件为: IP.src == 140.207.54.68 || IP.dst == 140.207.54.68 || IP.src == 140.207.69.61 || IP.dst == 140.207.69.61。过滤的结果如下图所示。

15.png
▲ 按照 IP 过滤的结果

点击 Statistics->Summary,就会统计过滤出的报文的总大小,即该域名下的流量。如下图所示。

16.png
▲ 按照域名过滤出的结果

当然,上面是手动分析方法,比较费时,而且无法在自动化测试脚本中实现,这里介绍一种 Python 实现的自动分析脚本的方法。 pcap2har 为一个分析 pcap 抓包的 Python 库文件,下载地址为 https://github.com/andrewf/pcap2har。下面为一个简单的 Python 示例程序,打印了 test.pcap 抓包中所有 HTTP 报文的 host,请求报文大小,响应报文大小。
#! /usr/bin/env python

# -*- encoding: utf-8 -*-
from pcap2har import pcap
from pcap2har import httpsession
dispatcher = pcap.EasyParsePcap('test.pcap')
session = httpsession.HttpSession(dispatcher)
for entry in session.entries:
request = entry.request
response = entry.response
print request.host
print len(request.msg.body)
print response.body_length

可以使用 Python 实现统计每个域名的 HTTP 流量。然后结合上面流量测试方法提到的 GT 的命令行方式进行抓包,我们将手机抓包,从手机上拷贝抓包文件,按照域名统计流量这三部分功能在自动化测试脚本中实现了,再结合手机时间加速方案,我们能很快的进行一轮测试,并得到本轮测试按照域名细分的结果。

经过了多轮测试取平均值的方式,我们得到了如下的结果如下表所示。

17.png
▲ 各域名对应的流量一览

我们测试的 APP 的后台服务器域名共有四个,其中大部分流量都集中在 www.test1.com 上,我们查看这部分报文的内容,发现是无法解析的,因为测试的 APP 与该服务器通信时采用了私有的协议进行报文传输,所以后面我们开始研究如何将这 540KB 流量按照功能来细分。

6.2域名下的流量按功能细分


对于报文采用私有协议实现的,要解析这部分报文,需要使用私有协议进行解析,我们 APP 的私有协议的实现方式是类似 Protobuf 的二进制编码方式,即每个协议的结构都是预先定义好的,每个协议都需要按照预定义的格式来解析,即如果协议报文的结构发生变化,解析的方式也是需要更新的。

一开始我们想到的方法是将报文解析算法集成到我们的自动化分析脚本中,这样就能将抓包的报文内容解析出来,但是这种方法带来的后果是自动化脚本中的报文解析算法需要实时同步主线版本中的内容,因为网络交互的报文后续的版本是会不断变化的,这样对于自动化脚本的维护成本就会太高。

后来我们想到了直接在 APP 中输出报文解析的结果,以日志的方式存储,后期自动化脚本获取日志来得到报文解析的结果。当然 APP 的报文日志打印功能需要增加配置,缺省是关闭的,正式版本不进行该内容打印,测试时将该配置打开,记录日志供自动化脚本使用。通过打印日志的分析,最终统计到的结果如下表所示。

18.jpg
▲ 各业务逻辑对应的流量一览

这里我们选取了 5 个最主要的流量消耗,其他还有很多流量消耗较少的报文,由于优化空间较小,不在此一一列举。至此,我们就得到了我们的产品 24 小时的消耗的背景流量总大小,以及按照域名和功能细分后的各个协议逻辑报文的大小,有了这些精细化的数据,我们才能更深一步的进行优化方案的分析。

好了,上篇就到这里结束了。如果你觉得写得还比较精彩,那么,欢迎移步看下篇《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)》(未完待续),下篇将详细介绍我们的具体分析方法和实践优化思路,以及在优化过程中总结出来的法则等。

(原文链接:点此进入,有删节)

附录:相关文章汇总


[1] 有关QQ、微信的技术文章:
微信后台团队:微信后台异步消息队列的优化升级实践分享
微信团队原创分享:微信客户端SQLite数据库损坏修复实践
腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率
腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)
腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(上篇)
微信Mars:微信内部正在使用的网络层封装库,即将开源
如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源
开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载]
微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解
微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)
微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)
Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]
微信团队原创分享:Android版微信从300KB到30MB的技术演进
微信技术总监谈架构:微信之道——大道至简(演讲全文)
微信技术总监谈架构:微信之道——大道至简(PPT讲稿) [附件下载]
如何解读《微信技术总监谈架构:微信之道——大道至简》
微信海量用户背后的后台系统存储架构(视频+PPT) [附件下载]
微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案
微信朋友圈海量技术之道PPT [附件下载]
微信对网络影响的技术试验及分析(论文全文)
一份微信后台技术架构的总结性笔记
架构之道:3个程序员成就微信朋友圈日均10亿发布量[有视频]
快速裂变:见证微信强大后台架构从0到1的演进历程(一)
快速裂变:见证微信强大后台架构从0到1的演进历程(二)
微信团队原创分享:Android内存泄漏监控和优化技巧总结
全面总结iOS版微信升级iOS9遇到的各种“坑”
微信团队原创资源混淆工具:让你的APK立减1M
微信团队原创Android资源混淆工具:AndResGuard [有源码]
Android版微信安装包“减肥”实战记录
iOS版微信安装包“减肥”实战记录
移动端IM实践:iOS版微信界面卡顿监测方案
微信“红包照片”背后的技术难题
移动端IM实践:iOS版微信小视频功能技术方案实录
移动端IM实践:Android版微信如何大幅提升交互性能(一)
移动端IM实践:Android版微信如何大幅提升交互性能(二)
移动端IM实践:实现Android版微信的智能心跳机制
移动端IM实践:WhatsApp、Line、微信的心跳策略分析
移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)
移动端IM实践:iOS版微信的多设备字体适配方案探讨
>> 更多同类文章 ……

[2] 有关QQ、微信的技术故事:
技术往事:创业初期的腾讯——16年前的冬天,谁动了马化腾的代码
技术往事:史上最全QQ图标变迁过程,追寻IM巨人的演进历史
开发往事:深度讲述2010到2015,微信一路风雨的背后
开发往事:微信千年不变的那张闪屏图片的由来
开发往事:记录微信3.0版背后的故事(距微信1.0发布9个月时)
一个微信实习生自述:我眼中的微信开发团队
>> 更多同类文章 ……

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:关于TCP可靠性传输特性为何在系统内核层实现的疑问下一篇:腾讯原创分享(三):如何大幅压缩移动网络下APP的流量消耗(下篇)

本帖已收录至以下技术专辑

推荐方案
评论 4
很详细,期待下篇
签名: 该会员没有填写今日想说内容.
先收藏,需要的时候再看,文章有点长
签名: 该会员没有填写今日想说内容.
很不错,学习了,期待中篇下篇
签名: 不错!!!!!!!!!!!!!!!
受教了,慢慢研究...
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部