linux js脚本无法响应,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=&current_citycode=&upload_time=2017-04-24+21%3A43%3A37&event_time=1493041418&session_id=1493041418&current_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改观了, 这是一个不错的工具, 往后还会继续深入学习!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值