PhantomJS: 一次顺序运转无回响反映的排查历程

背景

近来刚打仗PhantomJS, 据说这东西是一个基于WebKit的服务器端JavaScript API,可以完成绝大部分浏览器的操纵, 如饥似渴就想练练手.于是就简朴写了一个递次, 简朴引见下:

需求: 经由过程phantomJS向一个网站提议要求, 而且纪录各资本加载的时刻,名字
思绪:
    1.经由过程 onResourceRequested,取得资本要求时刻t1, 并经由过程资本ID,纪录在关联数组内
    2.经由过程 onResourceReceived 取得资本相应时刻t2, 也一样存到关联数组内
    2.用t2 - t1得出的, 就是资本加载消费的毫秒值
    3.由于资本ID是相关联的,所以只须要取恣意一个关联组的url就可以

递次源码

test.js 内容以下:

// 加载库
var page = require('webpage').create();
var system = require('system');
var req = {};  // 要求关联数组
var res = {};  // 相应关联数组
var num = 0;   // 用于纪录资本数
var address = 'http://www.yaochufa.com';

// 在 onResourceRequested 事宜绑定行动
page.onResourceRequested = function(request){
    num++;   
    req[request.id] = {'url': request.url, 'time': request.time};
};

// 在 onResourceReceived 事宜绑定行动
page.onResourceReceived = function(response){
    res[response.id] = {'url': response.url, 'time': response.time};
};


// 最先要求
page.open(address, function(status){
    
    if(status !== 'success')
    {
        // 假如要求胜利, 退出
        console.log(status);
        phantom.exit(); 
    }
    else{
        console.log('主页加载终了');
    }
    // 依据前面纪录的资本要求个数, 最先统计各资本的加载时刻
    for(i=1;i<=num;i++)
    {
        // 掏出要求数组的资本最先时刻, 并转换成时刻戳
        var req_time = new Date(req[i]['time']).getTime();

        // 掏出相应数组的资本完毕时刻, 并转换成时刻戳
        var res_time = new Date(res[i]['time']).getTime();
        
        // 作差就是加载的时刻
        var diff = res_time - req_time;
        console.log('ID:s',i, 'Loadtime:', diff, 'ms');

        // 打印对应ID的url
        console.log(res[i]['url']);
    }
    phantom.exit();

});

题目重现

phantomjs test.js

# 效果输出:
ReferenceError: Can't find variable: sfCode

  http://qiniu-cdn7.jinxidao.com/assets/js/index.js?v=201704201643:1
主页加载终了
ID:s 1 Loadtime: 73 ms
http://www.yaochufa.com/
ID:s 2 Loadtime: 50 ms
http://qiniu-cdn7.jinxidao.com/assets/css/common.css?v=201704201643
ID:s 3 Loadtime: 54 ms
http://qiniu-cdn7.jinxidao.com/assets/css/index.css?v=201704201643
ID:s 4 Loadtime: 80 ms
http://qiniu-cdn7.jinxidao.com/assets/js/jquery.js
ID:s 5 Loadtime: 78 ms
http://qiniu-cdn7.jinxidao.com/assets/js/lazyload.js
ID:s 6 Loadtime: 78 ms
http://qiniu-cdn7.jinxidao.com/assets/js/common.js?v=201704201643
ID:s 7 Loadtime: 150 ms
http://qiniu-cdn7.jinxidao.com/assets/js/index.js?v=201704201643
ID:s 8 Loadtime: 79 ms
http://qiniu-cdn7.jinxidao.com/js/user_analytics_pc.js?v=201704201643
ID:s 9 Loadtime: 154 ms
http://qiniu-cdn7.jinxidao.com/js/zhugeio_config.js?v=201704201643
ID:s 10 Loadtime: 106 ms
http://cdn7.jinxidao.com/images/icon.png?v=1.3
ID:s 11 Loadtime: 104 ms
http://cdn.jinxidao.com/images/icon/photo-app.png
ID:s 12 Loadtime: 100 ms
http://static.anquan.org/static/outer/image/hy_124x47.png
ID:s 13 Loadtime: 91 ms
http://cdn7.jinxidao.com/www/images/loading.gif
ID:s 14 Loadtime: 91 ms
http://cdn7.jinxidao.com/images/logo.png
ID:s 15 Loadtime: 94 ms
http://cdn7.jinxidao.com/www/images/weixin_code.png
ID:s 16 Loadtime: 92 ms
http://cdn7.jinxidao.com/www/images/footer-links.png
ID:s 17 Loadtime: 94 ms
http://cdn7.jinxidao.com/www/images/weixin_code.png?v=1.0
ID:s 18 Loadtime: 91 ms
http://cdn7.jinxidao.com/www/images/web-trust/alipay.png
ID:s 19 Loadtime: 91 ms
http://cdn7.jinxidao.com/www/images/web-trust/zhizhao.jpg
ID:s 20 Loadtime: 93 ms
http://cdn7.jinxidao.com/www/images/web-trust/chengxin.jpg
ID:s 21 Loadtime: 94 ms
http://cdn7.jinxidao.com/www/images/web-trust/kexin.jpg
ID:s 22 Loadtime: 87 ms
http://cdn.jinxidao.com/images/white_bg.png
ID:s 23 Loadtime: 91 ms
http://cdn.jinxidao.com/detail/appCode.png
ID:s 24 Loadtime: 94 ms
http://cdn.jinxidao.com/detail/appCode.png?v=3
ID:s 25 Loadtime: 64 ms
http://www.yaochufa.com/ajax/checkcity?v=1493041417771&callback=jQuery1113015539110638201237_1493041417766&_=1493041417767
ID:s 26 Loadtime: 64 ms
http://tj.yaochufa.com/kafkahttp/kafka/log?callback=jQuery1113015539110638201237_1493041417768&event_content=http%3A%2F%2Fwww.yaochufa.com%2F&log_id=1&app_id=4&longitude=&latitude=&user_id=¤t_citycode=&upload_time=2017-04-24+21%3A43%3A37&event_time=1493041418&session_id=1493041418¤t_url=http%3A%2F%2Fwww.yaochufa.com%2F&from_url=&use_agent=Mozilla%2F5.0+(Unknown%3B+Linux+x86_64)+AppleWebKit%2F538.1+(KHTML%2C+like+Gecko)+PhantomJS%2F2.1.1+Safari%2F538.1&visit_ip=114.55.70.153&cookie=38e73c28-09f1-2945-47d8-cbd70f3d72ff&arg2=&event_id=700000&_=1493041417769
ID:s 27 Loadtime: 73 ms
http://sdk.zhugeio.com/zhuge-lastest.min.js?v=2017324
ID:s 28 Loadtime: 136 ms
http://hm.baidu.com/hm.js?84c5b2688d39b4e3c23d132b53b4e79b
ID:s 29 Loadtime: 151 ms
https://apipool.zhugeio.com/web_event/?method=web_event_srv.upload&event=%7B%22type%22%3A%20%22statis%22%2C%22sdk%22%3A%20%22web%22%2C%22sdkv%22%3A%20%221.3.0%22%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22ak%22%3A%20%22b744d4b142c942c09cdc5095ba060824%22%2C%22did%22%3A%20%2215ba0340eac38a-04e727ec2-273f781b-c0000-15ba0340ead40b%22%2C%22ts%22%3A%201493041417.903%2C%22debug%22%3A%201%2C%22data%22%3A%20%5B%0A%20%20%20%20%7B%22et%22%3A%20%22ss%22%2C%22sid%22%3A%201493041417.902%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22pr%22%3A%20%7B%22os%22%3A%20%22Linux%22%2C%22br%22%3A%20%22Safari%22%2C%22rs%22%3A%20%221024*768%22%2C%22url%22%3A%20%22http%3A%2F%2Fwww.yaochufa.com%2F%22%7D%7D%0A%5D%7D&_=1493041417904
卡住...

  前面的输出, 我们已看到已根据我们需求那样, 得出资本ID, 资本加载时刻, 资本URL, 然则很新鲜的事, 到了也许是30的时刻, 就卡住了, 这里一定不是递次运转完, 由于递次末端有个退出, 假如是平常完毕了, 应当就退出了.所以我以为这个一定有那里失足了! 这也是让我以为phantomJS很不好的处所, 报错也不给个显著的毛病提示, 就在那里一动不动的.
  既然phantomJS不显著报错, 我们只能一步步伐试了, 之前谷歌上看到, phantomJS可以用debug形式运转, 然后经由过程接见特定端口来用浏览器调试, 然则在这不可, 由于浏览器压根打不开谁人链接, 一向在转, 也不晓得是什么缘由. 所以我们这里只能用console.log来排错了!
  由于打印输出的代码是在末了面的, 那末可以证实前面的事宜是准确被实行的, 由于假如前面的事宜失利了, 那末全部递次一定是不会打印的,相反的而是会停在前面卡住了.
为了证实这一点, 我们把for轮回去掉, 源码不再反复, 直接贴效果:

[root@iZ23pynfq19Z phantomjs-2.1.1-linux-x86_64]# ./bin/phantomjs 4.js 
ReferenceError: Can't find variable: sfCode

  http://qiniu-cdn7.jinxidao.com/assets/js/index.js?v=201704201643:1
主页加载终了

嗖的一声就完毕了, 这就考证了我们的猜测, 失足的代码应当是在for的打印内里, 既然晓得也许的接见, 我们for内里的每句代码都解释掉, 一句句零丁实行, for轮回体代码分别是:

    for(i=1;i<=num;i++)
    {
        // 掏出要求数组的资本最先时刻, 并转换成时刻戳
        var req_time = new Date(req[i]['time']).getTime();  
    }
    phantom.exit();

---- 试验1效果: 递次顺遂输出并完毕 ----

    for(i=1;i<=num;i++)
    {
        // 掏出相应数组的资本完毕时刻, 并转换成时刻戳
        var res_time = new Date(res[i]['time']).getTime();
    }
    phantom.exit();  
    
---- 试验2效果: 递次卡住了!!  ----

可以看出, 题目应当是涌现在第二句猎取时刻的处所, 然则这里应当是没题目的, 由于在试验时, 别的时刻都能顺遂转换! 然则没办法, 只要试验才给出证据! 我们将这句话拆分两句运转

    for(i=1;i<=num;i++)
    {
        // 掏出相应数组的资本完毕时刻, 并转换成时刻戳
        var res_time = new Date(res[i]['time'])
        console.log(res_time);
        console.log(res_time.getTime());
    }
    phantom.exit();
    
## 递次输出:
...
Mon Apr 24 2017 22:18:58 GMT+0800 (CST)
1493043538945
Mon Apr 24 2017 22:18:58 GMT+0800 (CST)
1493043538932
Mon Apr 24 2017 22:18:58 GMT+0800 (CST)
1493043538946
Mon Apr 24 2017 22:18:59 GMT+0800 (CST)
1493043539087
卡住了...

看来题目真的涌现在这题目, 然则如许照样不能看出题目啊, 由于变量都平常输出了! 思来想去都以为找不到题目, 只能继承往上拆了, 它是经由过程i在相应关联数组掏出对象, 所以我们在上面加一句打印对象:

    for(i=1;i<=num;i++)
    {
        // 掏出相应数组的资本完毕时刻, 并转换成时刻戳
        console.log(res[i]['time']);
        var res_time = new Date(res[i]['time'])
        console.log(res_time);
        console.log(res_time.getTime());
    }
    phantom.exit();
    
## 递次输出:
...
Mon Apr 24 2017 22:26:21 GMT+0800 (CST)
1493043981129
{"url":"https://apipool.zhugeio.com/web_event/?method=web_event_srv.upload&event=%7B%22type%22%3A%20%22statis%22%2C%22sdk%22%3A%20%22web%22%2C%22sdkv%22%3A%20%221.3.0%22%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22ak%22%3A%20%22b744d4b142c942c09cdc5095ba060824%22%2C%22did%22%3A%20%2215ba05b2b444b7-0c599ec6d-273f781b-c0000-15ba05b2b454b1%22%2C%22ts%22%3A%201493043981.128%2C%22debug%22%3A%201%2C%22data%22%3A%20%5B%0A%20%20%20%20%7B%22et%22%3A%20%22ss%22%2C%22sid%22%3A%201493043981.126%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22pr%22%3A%20%7B%22os%22%3A%20%22Linux%22%2C%22br%22%3A%20%22Safari%22%2C%22rs%22%3A%20%221024*768%22%2C%22url%22%3A%20%22http%3A%2F%2Fwww.yaochufa.com%2F%22%7D%7D%0A%5D%7D&_=1493043981128","time":"2017-04-24T14:26:21.273Z"}
Mon Apr 24 2017 22:26:21 GMT+0800 (CST)
1493043981273
Mon Apr 24 2017 22:26:21 GMT+0800 (CST)
1493043981132
undefined
递次卡住..

找到缘由

欣喜发明undefined, 嗯! 应当是undefined没有getTime要领, 致使递次失足了.看了下对应的资本ID是30, 而对应的url是:https://apipool.zhugeio.com, 不过为何这个对象是undefined呢? 而要求资本那块确实是有的, 谷歌了下, 发明这个是一个资本监控网站, 也就是相似帮别人测试资本加载速率的, 岂非这个要求是没有返回的? 先不论, 既然它是undefined. 那就经由过程推断过滤掉吧:

    for(i=1;i<=num;i++)
    {
        // 掏出要求数组的资本最先时刻, 并转换成时刻戳
        var req_time = new Date(req[i]['time']).getTime();
        // 过滤undefned
        if(res[i])
        {
            // 掏出相应数组的资本完毕时刻, 并转换成时刻戳
            var res_time = new Date(res[i]['time']).getTime();
            // 作差就是加载的时刻
            var diff = res_time - req_time;
            console.log('ID: ',i, 'Loadtime:', diff, 'ms');
            // 打印对应ID的url
            console.log(res[i]['url']);
        }
        else
        {
            console.log('ID: ' + i + ' no response');
        }

    } 
--- 递次输出 ---
.....
ID: 30 no response
ID:  31 Loadtime: 148 ms
https://apipool.zhugeio.com/web_event/?method=web_event_srv.upload&event=%7B%22type%22%3A%20%22statis%22%2C%22sdk%22%3A%20%22web%22%2C%22sdkv%22%3A%20%221.3.0%22%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22ak%22%3A%20%22b744d4b142c942c09cdc5095ba060824%22%2C%22did%22%3A%20%2215ba0698d3a9f4-0797b7d87-273f781b-c0000-15ba0698d3b4b2%22%2C%22ts%22%3A%201493044923.712%2C%22debug%22%3A%201%2C%22data%22%3A%20%5B%0A%20%20%20%20%7B%22et%22%3A%20%22info%22%2C%22pr%22%3A%20%7B%22os%22%3A%20%22Linux%22%2C%22br%22%3A%20%22Safari%22%2C%22rs%22%3A%20%221024*768%22%2C%22url%22%3A%20%22http%3A%2F%2Fwww.yaochufa.com%2F%22%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%7D%7D%0A%5D%7D&_=1493044923712
ID:  32 Loadtime: 37 ms
.....

可以看到, 这个剧本可以平常运转了.
  不过虽然可以运转了, 照样很猎奇是否是真的有资本只要要求, 而没有相应的, 但是现实并非! 在后面的测试中, 我发明, 假如我们在onResourceReceived打印资本ID和对象时, 会发明, 我们前面丧失的30号对象, 也是有输出的, 我们来试下:

page.onResourceReceived = function(response){
    console.log('ID: ' + response.id);
    // 将对象转变成JSON字符串
    console.log(JSON.stringify(response));
    res[response.id] = {'url': response.url, 'time': response.time};
};
--- 递次输出 ---
....(前面打印耗时)

// 零丁打印
ID: 30
{"contentType":null,"headers":[],"id":30,"redirectURL":null,"stage":"end","status":null,"statusText":null,"time":"2017-04-24T14:52:08.402Z","url":"https://api.zhugeio.com/v1/events/codeless/appkey/b744d4b142c942c09cdc5095ba060824/platform/3?event_url=%2F"}

...

可以看出, 资本ID:30是有相应的, 只是相应的比较慢罢了, 当最先运转轮回体时, 它还没完成写, 由于JS时尽人皆知的异步编程, 所以它并不像我们平常递次那样递次实行, 而是经由过程回调的体式格局完成任务.

代码小优化

  既然晓得它也是有相应的的, 那末我们就不能扬弃它! 由于它也是我们的一分子! 然则我们该怎么做呢? 无法之下去看PhantomJS的官网, 看到一个树模例子内里用到一个事宜:onLoadFinished, 字面意义就是完成加载时, 这个看起来就是我们要找的, 由于假如页面加载终了, 那末资本方面一定是已收齐(菜鸟明白), 那我们最先革新适才的剧本的, 将剧本的for分离出来, 放到 onLoadFinished 事宜中, 代码以下:

page.onLoadFinished = function(status){
    console.log('加载终了, 状况: ' + status);
    for(var i=1;i<=num;i++)
    {

        var req_time = new Date(result[i].time).getTime();
        if (rest[i])
        {
            var rest_time = new Date(rest[i].time).getTime();
            var diff = rest_time - req_time;
            console.log('ID:s',i, 'Loadtime:', diff, 'ms');
        }
        else
            // 色彩提示
            console.log('\033[31mNo such response!\033[0m');
        console.log(result[i].url);
    }

};

经由上面的革新, 剧本已可以比较好的算出加载时刻了,不过我们有些处所还须要优化, 那就是输入地点那里, 由于地点是写死的, 所以假如我们用来测试别的站点时, 就必须要修正代码, 这简直就是一个大毛病! 所以我们须要system库!, 完全代码以下:

// 加载库
var page = require('webpage').create();
var system = require('system');
var req = {};  // 要求关联数组
var res = {};  // 相应关联数组
var num = 0;   // 用于纪录资本数
var args = system.args;
if (args.length !== 2)
{
    console.log('\033[31mUsage: Url ( http://www.baidu.com )\033[0m');
    phantom.exit();
}
var address = args[1];

// 在 onResourceRequested 事宜绑定行动
page.onResourceRequested = function(request){
    num++;
    req[request.id] = {'url': request.url, 'time': request.time};
};

// 在 onResourceReceived 事宜绑定行动
page.onResourceReceived = function(response){
    res[response.id] = {'url': response.url, 'time': response.time};
};

page.onLoadFinished = function(status){
    console.log('加载终了, 状况: ' + status);
    // 依据前面纪录的资本要求个数, 最先统计各资本的加载时刻
    for(var i=1;i<=num;i++)
    {
     var req_time = new Date(req[i].time).getTime();
        if (res[i])
        {
            var rest_time = new Date(res[i].time).getTime();
            var diff = rest_time - req_time;
            console.log('ID:s',i, 'Loadtime:', diff, 'ms');
        }
        else
            console.log('\033[31mNo such response! ID: ' + i + '\033[0m');
        console.log(req[i].url);
    }

};

// 最先要求
page.open(address, function(status){

    if(status !== 'success')
    {
        // 假如要求胜利, 退出
        console.log(status);
        phantom.exit();
    }
    else{
        console.log('主页加载终了');
    phantom.exit();
    }

});

不过照样有时刻照样会涌现找不到谁人相应慢的要求, 能够须要换种完成的思绪或许越发切近的事宜, 不过这个用来练手真是觉得, 之前一向以为PhantomJS卡死, 实在只是递次错了, 此次对phantomJS改变了, 这是一个不错的东西, 今后还会继承深切进修!
迎接列位大神指导交换,转载请说明泉源:https://segmentfault.com/a/11…

    原文作者:Lin_R
    原文地址: https://segmentfault.com/a/1190000009182431
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞