美文网首页
基于远程调试协议的H5性能测试解决方案

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

作者: EchoDing | 来源:发表于2016-09-13 19:43 被阅读0次
    立体

    前言

    为了丰富动态性,移动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连接后,通过刷新页面直接可获取性能参数报告,对测试效率的提升带来了极大帮助。目前,为了进一步满足测试的需求,该方案还在精细化调整中,读者有什么建议或意见欢迎留言提出。

    相关文章

      网友评论

          本文标题:基于远程调试协议的H5性能测试解决方案

          本文链接:https://www.haomeiwen.com/subject/apnmettx.html