内容简介:因为正在开发一个项目,而这个项目使用到了虽然但是这里有个需求是,当网站崩溃时,我要做出一些操作。但是目前网上没有一个好的办法去监听当前网站是否崩溃。
因为正在开发一个项目,而这个项目使用到了 puppeteer
,其中有个功能是在 puppeteer
打开的chrome里打开多个 Tab
,并进行管理。
虽然 puppeteer
可以打开多个网站,但是并不利于管理,所有我使用的是插件的方式,通过插件来打开多网站,并进行管理。
但是这里有个需求是,当网站崩溃时,我要做出一些操作。但是目前网上没有一个好的办法去监听当前网站是否崩溃。
可能有同学会说: puppeteer
不是提供了一个 page.on('error', fn)
的方法,来进行监听么?
请注意上文中提到的,使用插件打开多个网站, puppeteer
提供的方法只能对自己打开的网站起作用,没有使用 puppeteer
打开的网站, page.on('error', fn)
方法无能为力。
使用Service Workers
这个方法是由我同事 Haitao 提出来的思路。
在当前网站上运行一个 Service Workers
,因为在运行的时候 Service Workers
会再启动一个单独的进程,当前网站和 Service Workers
是两个单独的进程。也就是说当网站崩溃时,并不影响 Service Workers
进程。所以可通过 心跳检测
来进行判断网站是否崩溃。
网上也有阿里的同学写的相关文章: 如何监控网页崩溃?
但是我并没有使用这个方式,因为当 Service Workers
崩溃了,那就没有任何办法了,可能有同学会说:网站和 Service Workers
互相发 心跳检测
。这可能是一种办法,但是我不太喜欢这种方式。
使用Webkit的远程调试协议
介绍
在开始前,我们先去看下 puppeteer
的源码,为什么 puppeteer
可以监听到网页的崩溃。
其代码在 lib/Page.js
文件里。
首先可以看到Page是一个 Class
,其继承了 EventEmitter
, EventEmitter
为 page
提供了 on
方法,也就是我们之前看到的: page.on('error', fn)
从这里就可知,在 Page Class
里,有地方调用了 this.emit('error')
来触发 error event
。搜了一下,发现其代码在 _onTargetCrashed
方法里。如:
触发 crash
的方法,我们找到了。那这个 _onTargetCrashed
又是在哪触发的呢?
可见,是一个叫 client
的方法监听到了 Inspector.targetCrashed
事件,而这个事件触发了 _onTargetCrashed
函数, clinet
方法就不再跟了,因为跳地方较多,只需要知道,最终 client
是一个 websocket
的产物。而 websocket
创建的代码在 lib/Launcher.js
里。 代码位置
注意这两行:
const transport = new PipeTransport((chromeProcess.stdio[3]), (chromeProcess.stdio[4])); connection = new Connection('', transport, slowMo);
chromeProcess
是 nodejs
中的 spawn
产物,代码为:
const chromeProcess = childProcess.spawn( chromeExecutable, chromeArguments, { detached: process.platform !== 'win32', env, stdio } );
其中 chromeArguments
是 chrome
启动的参数列表,此列表是有一个 --remote-debugging-
的:
if (!chromeArguments.some(argument => argument.startsWith('--remote-debugging-'))) chromeArguments.push(pipe ? '--remote-debugging-pipe' : '--remote-debugging-port=0');
现在就明朗多了, Inspector.targetCrashed
这个事件,是由 Webkit远程调试协议
也就是 remote debugging protocol
提供的。
其定义在webkit的 Inspector.json
里: Source/WebCore/inspector/Inspector.json#L39-L42
关于这个 event
的commit url为: https://github.com/WebKit/webkit/commit/255ba17d1d7e0ad1530d503f28ee5d93d7c5351e#diff-4681ce2c9384e770dfac03ab133f133b
编写解决方案代码
现在我们知道了,只要能监听到 Inspector.targetCrashed
事件,就可以知道网站是否关闭了。我们先在 puppeteer
的启动参数里,增加一行启动参数:
puppeteer.launch({ '--remote-debugging-port=9222', // other args });
当 puppeteer
启动时,会监听本地的 9222
端口,其中路径 /json
为当前的详情。如:
其格式为:
[ { "description": "", "devtoolsFrontendUrl": "/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/A1CB5A9CC25A7EE8A99C6A4A1876E4D3", "faviconUrl": "https://s.ytimg.com/yts/img/favicon_32-vflOogEID.png", "id": "A1CB5A9CC25A7EE8A99C6A4A1876E4D3", "title": "張三李四 Chang and Lee 【等無此人 Waiting】 - YouTube", "type": "page", "url": "https://www.youtube.com/watch?v=lAcUGvpRkig&list=PL3p0C_7POnMHG-b0dzkeTVdNuM6yRE5iQ&index=10&t=0s", "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/A1CB5A9CC25A7EE8A99C6A4A1876E4D3" }, // other {
其中的 type
为当前进程的详情:
- page: 网页
- iframe: 网页嵌套的iframe
- background_page: 插件页面
- service_worker: Service Workers
这个type的作用在于,你只想监听某一类型的崩溃。
还有一个更主要的字段: webSocketDebuggerUrl
。我们将使用这个字段的值,来进行获取消息。有一个简单的demo:
const http = require('http'); const WebSocket = require('ws'); http.get('http://127.0.0.1:9222/json', res => { res.addListener('data', data => { const result = JSON.parse(data.toString()); result.forEach(info => { const client = new WebSocket(info.webSocketDebuggerUrl); client.on('message', data => { if (data.indexOf('"method":"Inspector.targetCrashed"') !== -1) { console.error('crash!'); } }); }); }) })
先看懂这段代码,后面的代码才好理解,因为代码过于简单,这里就不再介绍了。
这段代码有个问题是,插件打开网站时,会存在一定的延迟,可能会导致某些网站没有被监听到,而且当这段代码运行后,插件再打开网站时,也不会监听到。针对这个问题,优化了下代码:
const http = require('http'); const WebSocket = require('ws'); module.exports = () => { const wsList = {}; let crashStaus = false; const getWsList = () => { return new Promise((resolve) => { http.get('http://127.0.0.1:9222/json', res => { res.addListener('data', data => { try { const result = JSON.parse(data.toString()); const tempWsList = {}; result.forEach(info => { if (typeof wsList[info.id] === 'undefined') { tempWsList[info.id] = info.webSocketDebuggerUrl; wsList[info.id] = info.webSocketDebuggerUrl; } }); if (Object.keys(tempWsList).length !== 0) { resolve(tempWsList); } } catch (e) { console.error(e); } }); }); }); }; setInterval(() => { getWsList().then(list => { Object.values(list).forEach(wsUrl => { const client = new WebSocket(wsUrl); client.on('message', data => { if (data.indexOf('"method":"Inspector.targetCrashed"') !== -1) { if (!crashStaus) { crashStaus = true; console.log('crash!!!'); } } }); }) }); }, 1000); };
其中需要说明一下这段代码:
if (!crashStaus) { crashStaus = true; console.log('crash!!!'); }
因为我的需求是,任何一个进程 crash
了,就关闭整个服务,重新运行。所以如果有多个进程同时 crash
了。我的代码只走一次,不想让他走多次。这个是针对我这里的需求,各位同学可以根据自己的需求更改代码。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
全景探秘游戏设计艺术
Jesse Schell / 吕阳、蒋韬、唐文 / 电子工业出版社 / 2010-6 / 69.00元
撬开你脑子里的那些困惑,让你重新认识游戏设计的真谛,人人都可以成为成功的游戏设计者!从更多的角度去审视你的游戏,从不完美的想法中跳脱出来,从枯燥的游戏设计理论中发现理论也可以这样好玩。本书主要内容包括:游戏的体验、构成游戏的元素、元素支撑的主题、游戏的改进、游戏机制、游戏中的角色、游戏设计团队、如何开发好的游戏、如何推销游戏、设计者的责任等。 本书适合任何游戏设计平台的游戏设计从业人员或即将......一起来看看 《全景探秘游戏设计艺术》 这本书的介绍吧!