基于远程调试协议的H5性能测试解决方案

立体

前言

为了丰富动态性,移动app一般会嵌入H5页面来实现实时操作,例如:支付,运营活动等。H5技术在扩展app功能方面占据重要地位。对于一个H5的产品,除了其实现功能是重点外,性能同样是用户体验中不可或缺的一环。H5性能的测试工具一般分为两类,一类是抓包工具,如Fiddler、Charles等;另一类是平台型工具,如PageSpeed、Chrome Devtools等。抓包类工具对于数据的展示不够直观,操作比较复杂。本文通过探索平台型工具的Chrome Devtools的实现机制,提供了基于远程调试协议的H5性能测试解决方案。

远程调试协议

Chrome Devtools是通过远程调试协议Remote debugging protocol)与浏览器页面(pages)交互和调试。采用webSocket来与页面建立通信通道,由发送给页面的commands和它所产生的events组成。这个协议是开放的,第三方开发者也可以调用这个协议来与页面交互调试。

WebSocket通信协议介绍

WebSocket Protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信。HTTP通信协议中,服务器需要接受到客服端的请求后被动作出响应。不同于传统通信协议,WebSocket协议在建立第一次握手协议后,服务器不需要等待请求在作出响应,而是在有新数据时就主动推送给服务端,实现了客服端与服务器,客户端与客户端之间的实时通信,更好的节省了服务器资源和带宽。

一张图带你体会高效的WebSocket Protocol:

以下是WebSocket Protocol的抓包实例:

引用百度百科图片

建立第一次的握手协议后,客户端和服务器之前就开始进行有效的实时通信了。

远程调试协议结构

远程调试协议基于WebSocket,利用WebSocket建立连接开发者和浏览器内核的快速数据通道。该协议把操作划分为不同的域(domain),比如Page、Rendering、Network、Memory和DOM等,通过远程调试协议发送的请求可通过域操作获取相应的信息。每个域(domain)定义了它所支持的command和它所产生的event。每个command包含了指定所要进行的操作以及操作说要的参数和描述;每个event表示了发生某个事件的监听,通过设立监听点来返回信息和进行下一步操作。command和event中可能涉及到非基本数据类型,在domain中被归为Type,比如:’LoaderId’: ,其中loaderId为非基本数据类型。下图

显示了远程调试协议network域的基本数据结构:

{

"domain": "Network",

"description": "Network domain allows tracking networkactivities of the page. It exposes information about http, file, data and otherrequests and responses, their headers, bodies, timing, etc.",

"dependencies": ["Runtime", "Security"],

"types": [{

"id": "LoaderId",

"type": "string",

"description": "Unique loader identifier."

},

..., {}

],

"commands": [{

"name": "clearBrowserCache",

"description": "Clears browser cache.",

"handlers": ["browser"]

},

..., {}

],

"events": [{

"name": "requestServedFromCache",

"description": "Firedif request ended up loading from cache.",

"parameters": [

{ "name":"requestId", "$ref": "RequestId","description": "Request identifier." }

]

},

..., {}

]

}

事实上,远程调试协议数据结构就是一种json格式的数组,可以通过json对象的方式调用其方法或事件。例如要调用network中清除浏览器缓存的方法,可以通过WebSocket协议连接到该页面后,使用WebSocket的send方法发送一个对象来实现,

ws.send('{"id": 1,"method": "Network.clearBrowserCache", "params":{}}')

其中ws表示WebSocket与浏览器建立连接后的url,id是被链接页面的id。

应用实践

了解了远程调试协议建立的通信机制和数据结构,接下来看看实战的例子——如何获取手机中H5页面的性能数据。

整个操作流程的系统框图

1.建立移动端和PC的连接,获取webSocketDebuggerUrl

手机端用chrome浏览器打开需调试的H5页面,通过USB和PC端连接,执行端口转发指令将页面发送的数据信息转发到9222端口,这样就可以在PC上操纵手机的页面。

adb forward tcp:9222 localabstract:chrome_devtools_remote

在PC端访问http://127.0.0.1:9222/json即可获得当服务端连接到该标签页的websocket地址,如下图:

"webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/2"即为要调试的页面

[ {

"description": "",

"devtoolsFrontendUrl": "http://chrome-devtools-frontend.appspot.com/serve_rev/@799386cbf7c1cbf70c12c0e593b2b2e23e39ba30/inspector.html?ws=127.0.0.1:9222/devtools/page/2",

"id": "2",

"title": "微微一笑很倾城,抽张油卡变男神!",

"type": "page",

"url": "http://202.69.27.140:13080/dmz/lottery-sd/modules/lottery-sd/1.0.0/views/index.html",

"webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/2"

} ]

2.初始化消息

为了获取页面传递数据,首先得建立一个初始化窗口,触发客户端和网络的连接,并建立监听的事件。

var socket = new WebSocket(url);

socket.onopen = function(event) {

//发送一个初始化消息

socket.send('{"id": 6, "method":"Network.enable", "params": {}}');

socket.send('{"id": 6, "method":"Page.enable", "params": {}}');

socket.send('{"id": 6, "method":"Page.navigate", "params":{"url":"http://202.69.27.140:13080/dmz/lottery-sd/modules/lottery-sd/1.0.0/views/index.html"}}');

//监听消息

socket.onmessage = function(event) {

console.log('Client received a message', event);

var data = event.data;

var obj = new Function("return" + data)();

//console.log(obj.method);

所有的数据操作要在这个初始化窗口内实现。

3.发送远程调试协议方法的消息

页面发送的消息要经过远程调试协议的“包装”来解析页面的性能数据。

function DoToMessage(message,page){

switch (message.method) {

case 'Page.domContentEventFired':

page.domContentEventFiredMs = message.params.timestamp * 1000;

break;

case 'Page.loadEventFired':

page.loadEventFiredMs = message.params.timestamp * 1000;

break;

default:

if (message.method.match(/^Network\./)) {

id = message.params.requestId;

switch (message.method) {

case 'Network.requestWillBeSent':

// the first is the originalrequest

if (typeofpage.originalRequestId === 'undefined' &&

message.params.initiator.type === 'other') {

page.originalRequestMs =message.params.timestamp * 1000;

page.originalRequestId =id;

}

page.objects[id] = {

'requestMessage':message.params,

'responseMessage':undefined,

'responseLength': 0,

'encodedResponseLength': 0,

'responseFinished':undefined,

'responseBody': undefined,

'responseBodyIsBase64':undefined

};

4.获取HAR文件,并进行对象的格式整理

页面向服务端发送了远程调试协议方法下的请求,服务端会返回HAR(HTTP Archive)文件,一个用来储存HTTP请求/响应信息的通用文件格式,基于JSON格式的数据。我们可以对改文件数据进行二次数据格式整理,便于后续的对象操作。例如每个输入会返回页面参数id,开始时间,加载时间,请求参数,响应参数等。

functionfromPage(page) {

for (var requestIdin page.objects) {

entries.push({

'pageref': page.id.toString(),

'startedDateTime': newDate(object.requestMessage.wallTime * 1000).toISOString(),

'time': toMilliseconds(duration),

'request': {

'method':object.requestMessage.request.method,

'url':object.requestMessage.request.url,

'httpVersion': protocol,

'cookies': [], // TODO

'headers':requestHeaders.pairs,

'queryString': queryString,

'headersSize':requestHeaders.size,

'bodySize':object.requestMessage.request.headers['Content-Length'] || -1,

},

'response': {

'status': object.responseMessage.response.status,

'statusText':object.responseMessage.response.statusText,

'httpVersion': protocol,

'cookies': [], // TODO

'headers':responseHeaders.pairs,

'redirectURL': redirectUrl,

'headersSize':responseHeaders.size,

'bodySize': bodySize,

'_transferSize':object.encodedResponseLength,

'content': {

'size':object.responseLength,

'mimeType':object.responseMessage.response.mimeType,

'compression': compression,

'text':object.responseBody,

'encoding':object.responseBodyIsBase64 ? 'base64' : undefined,

}

........

}

}

5.从HAR文件中提取需要展示的性能参数

functionGetOverViews(har) {

var getreqstime = function(har) {

var timess = 0;

for (var i = 0; i

timess = timess +har.entries[i].time;

return timess;

};

}

return {

'overview': {

'countoftime': getreqstime(har),

},

'entries': har.entries

};

}

例如通过统计har中所有entries的时间总和来获取当前页面的加载总时间,并将该结果(getreqstime(har))赋值到'contoftime'对象中,前端页面通过调用countoftime即可展示对应的数据。

6.结果展示

总结

本文通过对远程调试协议的应用,介绍了一种简便获取H5页面性能测试数据的解决方案。通过这种方式,测试人员不需要使用第三方工具对H5页面进行调试,而是在建立移动端和PC连接后,通过刷新页面直接可获取性能参数报告,对测试效率的提升带来了极大帮助。目前,为了进一步满足测试的需求,该方案还在精细化调整中,读者有什么建议或意见欢迎留言提出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容