Qtest测试之道 移动 H5 广告的性能自动化测试方案 (续)
书接上文
上篇文章《移动 H5 广告的性能自动化测试方案》末尾我提到了几个问题,是我们在探索移动端 H5 性能自动化测试遇到的几个比较棘手的坑:
- Tcpdump 需要 root 权限,而我们的任务需要分布式执行,且目前高版本的手机 root 失败率非常高,而且还存在 root 被还原的情况 。
- JS 注入以什么方式进行?
- pcap 文件的解析对 https 的请求束手无策,而我们的广告请求都是 https 的。
- 白屏时间是否是标准答案?
本文主要介绍我们的解决思路,以供读者参考,如有问题欢迎大家轻喷。。
一些前端性能的背景知识
在介绍新方案之前,想先扯一些前端的背景知识。
关于移动浏览器
目前手机上的浏览器可谓琳琅满目,譬如 360 安全浏览器,UC 浏览器,QQ 浏览器,欧朋浏览器,百度手机浏览器,谷歌浏览器,搜狗手机浏览器,猎豹浏览器等等,给测试同学的适配带来了巨大的工作量。另外最近 “原创的红芯浏览器” 可谓刷爆了各大朋友圈,在此也想小科普一些浏览器内核的知识。
早期全球仅有四个独立的浏览器内核,分别为微软 IE 的 Trident、网景最初研发后卖给 Mozilla 基金会并演化成火狐的 Gecko、KDE 的开源内核 Webkit 以及 Opera(欧朋) 的 Presto。其中,Presto 是历史最悠久的内核。
早期的时候 chrome 和 safari 都采用了 webkit 内核,不过 Google 发布了 chrome 浏览器后,将使用的浏览器内核更名为 chromium,是 webkit 开源项目的一个分支,随后中2013年4月3日,谷歌的 Chromium Blog 发表了一篇博客,表示后续 Chromium 项目将采用 Blink 渲染引擎。
Android 原生浏览器、苹果的 Safari、谷歌的 Chrome(Android4.0 使用) 都是基于 Webkit 开源内核开发的。
浏览器的核心组件
用户界面: 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
浏览器引擎: 在用户界面和呈现引擎之间传送指令。
渲染引擎: 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
网络层: 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
用户界面后端: 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
JavaScript 解释器: 用于解析和执行 JavaScript 代码。
数据存储: 这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了 “网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。
以 WebKit 内核为例,看一下浏览器渲染的过程
-
W3C performance API
前文有提到影响用户体验的三个性能指标分别是:- Start Render
- DOM Ready
- Page Load
这几个关键时间节点,可以通过 W3C performance API 中的 Navigation Timing API 来获得。
W3C 组织在 2010 年成立了 web 性能工作组,专门开发了一个支持将浏览器暴露给 JavaScript 的 API,该 API 为浏览器开发人员提供了一种精确度量和分析 web 页面性能的有效手段。整套 performance API 有 10 种
- Navigation Timing: 能够帮助网站开发者检测真实用户数据(RUM),例如带宽、延迟或主页的整体页面加载时间。
- Resource Timing: 对单个资源的计时,主要统计比如 DNS、TCP 链接、wating 等具体的耗时。
我们需要统计的时间指标可以通过以下公式换算出来:
DNS查询耗时 :domainLookupEnd - domainLookupStart
TCP链接耗时 :connectEnd - connectStart
request请求耗时 :responseEnd - responseStart
解析dom树耗时 : domComplete - domInteractive
白屏时间 :responseStart - navigationStart
DOMReady时间 :domContentLoadedEventEnd - navigationStart
onload时间 :loadEventEnd - navigationStart
关于白屏时间的争议
负责性能数据监控的同学在开发过程中发现,白屏数据按照上面时间点抓取存在问题,不太准确,有的时候拿到的截图已经开始有 dom 数据加载了,为此还发生过争论,带着这个问题仔细的考证了一下,从 W3C 开源项目的 issue 列表中看到了项目成员的解答:
问:为什么没把first paint time 写进标准里呢?希望您能把他加进去。
答:因为不能准确定义它,这个时间标准是模糊的。比如一些网页,他本来就是空白的啊,怎么来定义这个paint的时间呢?而且很多人并不在乎,什么时候画出第一个像素,他们想跟踪一下具体某个元素是什么时候画出来的。但是每个网站都不一样,我们不可能来定义太细的接口。所以不能加。
Frame Timing 可以让你跟踪画出来的时间,如果你真想追踪first paint(白屏) time,你得等我们有一些 Houdini pieces(巧妙的规避办法 –-牛津大字典) 才能有比较好的解决办法。
问:那为什么DevTools里面有呢?为什么不加个时间戳(从hidden到visible)? 我就是想知道,从点击进去到看到内容,用户等了多久,这个值是模糊的么?
答:在DevTools里有,并不是意味着他是一个好的标准。如果想加时间戳来记录,你自己加就行了呗。我只是认为,first paint 不够分量来写进标准,frame Timing 应该更有意义。但是需要时间。
如果读者有不错的白屏时间计算方法,欢迎做文章下面留言。
移动端 H5 性能测试方案比较
以下几种方案是我们内部讨论过程中梳理过的几种可能性,有些方案可能不适合我们当前的需求,但是适合读者自身,所以观点仅仅给大家作为参考。
1. Fiddler/Charles 进行性能抓包
- 优点:测试人员熟悉功能,上手快,UI 功能完善友好,可以通过 script 扩展
- 缺点:性能自动化方案实施成本比较高,跨平台支持比较差
2. PhantomJS 抓包
- 优点:PhantomJS(http://phantomjs.org/ ;)是一个可编程的无界面浏览器。它通过 JavaScript 和 CoffeeScript 控制 webkit 的各个模块,比如 CSS Selector,JSON,Canvas、SVG 和 HTTP 网络等等,它通过 netsniff.js 监控网络请求并生成 HAR 文件,拿到 HAR 文件后续将很好办了。
- 缺点:基于 PC 端出的性能数据
3.Chrome remote debug 远程调试
- 优点:通过 Chrome DevTools Protocol 来打通 PC 浏览器和移动设备,功能强大毋庸置疑,我们开发基本上采用这种方式来进行移动端调试和优化
- 缺点:无法自动化运行,需要翻樯,因为 chrome inspect 需要访问 appspot.com 地址
4. Tcpdump&Mimtproxy
- Tcpdump 优点 相信需要测试移动网络抓包场景的同学一定有接触过 tcpdump 吧?Tcpdump 是 Linux 系统中使用的比较广泛的一款抓包工具,其利用的是 libpcap 工具,通过监听和记录网卡接口的特定端口的网络数据来实施抓包,生成的文件以 pcap 结尾。用 wireshark 直接读取或者通过解析 pcap2har 工具来生成需要的性能数据,是比较接近真实移动设备环境的一种测试手段。具体的操作方式可以自行搜索,也可以参考我写的上一篇文章的用法介绍。
Tcpdump 缺点:需要 root、高版本手机 root 成功率极低、容易被还原导致自动化测试任务无法进行,无法解析 https 请求。
MimtProxy 优点
MitmProxy 是什么这里就不做赘述,读者可以自己看一下官网介绍 (https://mitmproxy.org; ),它本来是被黑客拿来做 “中间人攻击” 用的利器,我们主要用到的是它对 https 数据截获的功能。不同于 tcpdump 的是我们可以对 mitmproxy 抓取到的数据做高度定制化处理。MimtProxy 缺点
由于其代理的特征,会对传输数据有一定性能上的损耗,不过从版本横向比较的角度来说可以忽略不计;另外 mitmproxy 获取不到 UDP 数据包;另外还有一些其他坑就不铺开说了。
关于浏览器的打开方式
监控 WebView 获取页面的性能数据
灵感来自 (https://github.com/jwcqc/WebViewMonitor;) 项目;
- 操作流程很简单:
- 通过在被测手机端安装 APP,接收服务端下发测试指令集告诉客户端开始进行 H5 测试
- 客户端根据指令打开指定浏览器内核的 WebView,在访问浏览器之前注入监控 js 到页面头部
- 客户端执行完成页面访问后,在本地记录测试数据,回传到服务端
目前支持 chrome 内核和 QQ 内核
设计流程图
-
基于 STFService APK 的改造
由于我们整体的测试方案是基于 OpenSTF 进行定制的,所以我们选择在 STFService APK 上进行改造,当然我们在开发过程中做了解耦,方便以后升级。
注入代码如下:public void onProgressChanged(int newProgress) { if (newProgress == 100) { try { if (mTimerFinish != null) { mTimerFinish.cancel(); mTimerFinish = null; } } catch (Exception e) { } if (!isDone) { mTimerFinish = new Timer(); mTimerFinish.schedule(new TimerTask() { @Override public void run() { //TODO collector.js文件的地址,收集的功能主要在这里面实现 String injectJs = "https://thisis.inject.js"; String js = "javascript:" + " (function() { " + " var script=document.createElement('script'); " + " script.setAttribute('type','text/javascript'); " + " script.setAttribute('src', '" + injectJs + "'); " + " document.head.appendChild(script); " + " script.onload = function() {" + " startWebViewMonitor();" + " }; " + " }" + " )();"; onLoadInjectJS(js); } }, 10000); } } }
-
性能数据处理代码
public long[] parsePerformanceData(String data) { long[] result = null; try { JSONObject jsonObject = new JSONObject(data); jsonObject = jsonObject.getJSONObject("payload"); JSONObject timeJsonObj = jsonObject.getJSONObject("navigationTiming"); final long timeStart = timeJsonObj.optInt("navigationStart"); final long timeWhite = timeJsonObj.optInt("responseStart") - timeStart; final long timeLoad = timeJsonObj.optInt("loadEventEnd") - timeStart; final long timeDNS = timeJsonObj.optInt("domainLookupEnd") - timeJsonObj.optInt("domainLookupStart"); final long timeTCP = timeJsonObj.optInt("connectEnd") - timeJsonObj.optInt("connectStart"); final long timeDOMParse = timeJsonObj.optInt("domComplete") - timeJsonObj.optInt("domInteractive"); final long timeDOMReady = timeJsonObj.optInt("domContentLoadedEventEnd") - timeJsonObj.optInt("navigationStart"); result = new long[]{timeWhite, timeLoad, timeDNS, timeTCP, timeDOMParse, timeDOMReady}; } catch (Exception e) { result = null; } return result; }
-
针对 OpenSTF 服务端做了扩展
- 在 STFService/wire.proto 中扩展了消息事件
- 修改了/lib/units/device/plugins/service.js 部分逻辑,增加了部分事件监听和接口
- 增加了服务端和客户端定制接口和数据处理逻辑 event 事件
性能数据的 Timline 瀑布图
通过以上方式拿到性能数据后,需要对 pacap 文件进行转化,一开始用的是开源工具 python 版本的 pcap2har,开源地址(https://github.com/andrewf/pcap2har; )后来为了更好的集成到平台中,我们尝试写了一套 node 版本的,具体参考公众号之前的文章《nodejs 实现 pcap 转换 har》,没错,上面的技术方案也是团队两个小伙子捣鼓出来的,是不是非常棒?
通过开源项目 harviewer(地址请参考我上篇文章)将 json 格式的结果文件快速转换成前端 timline 格式的报告,当然为了满足我们项目的需要,一样对 harview 进行了大量的改造,代码细节就不截图了。
测试报告如下:
结束语
前端性能优化
前端性能优化方案,请关注我们部门前端团队奇舞团的公众号,有专业的文章讲解,比如 l 来自 W3C 成员的文章《CSS 性能优化的 8 个技巧》等等。
一些疑问
- 项目历时大半年时间
- 基于开源项目 openstf,对其做了部分重构(前端和数据库)
- 大家遇到的问题我们也多数碰到了,比如性能问题、掉线问题、offline 问题等等
参考文献
https://blog.csdn.net/TMQ1225/article/details/53204476
https://segmentfault.com/a/1190000004010453
http://www.cocoachina.com/ios/20170717/19882.html?utm_source=itdadao&utm_medium=referral
https://github.com/jwcqc/WebViewMonitor
https://github.com/w3c/navigation-timing/issues/21
https://www.w3.org/Submission/first-screen-paint/
https://www.cnblogs.com/littlelittlecat/p/6810294.html