提交 9842a24f 编写于 作者: Huan (李卓桓)'s avatar Huan (李卓桓)

seprate watchdog code to indenpendent file

上级 018c3c47
...@@ -68,7 +68,11 @@ function onBrowserDead(e) { ...@@ -68,7 +68,11 @@ function onBrowserDead(e) {
this.isBrowserBirthing = true this.isBrowserBirthing = true
const TIMEOUT = 180000 // 180s / 3m const TIMEOUT = 180000 // 180s / 3m
this.watchDog(`onBrowserDead() set a timeout of ${Math.floor(TIMEOUT / 1000)} seconds to prevent unknown state change`, {timeout: TIMEOUT}) // this.watchDog(`onBrowserDead() set a timeout of ${Math.floor(TIMEOUT / 1000)} seconds to prevent unknown state change`, {timeout: TIMEOUT})
this.emit('watchdog', {
data: `onBrowserDead() set a timeout of ${Math.floor(TIMEOUT / 1000)} seconds to prevent unknown state change`
, timeout: TIMEOUT
})
log.verbose('PuppetWebEvent', 'onBrowserDead(%s)', e.message || e) log.verbose('PuppetWebEvent', 'onBrowserDead(%s)', e.message || e)
if (!this.browser || !this.bridge) { if (!this.browser || !this.bridge) {
...@@ -112,7 +116,8 @@ function onBrowserDead(e) { ...@@ -112,7 +116,8 @@ function onBrowserDead(e) {
function onServerDing(data) { function onServerDing(data) {
log.silly('PuppetWebEvent', 'onServerDing(%s)', data) log.silly('PuppetWebEvent', 'onServerDing(%s)', data)
this.watchDog(data) // this.watchDog(data)
this.emit('watchdog', { data })
} }
function onServerScan(data) { function onServerScan(data) {
...@@ -131,7 +136,8 @@ function onServerScan(data) { ...@@ -131,7 +136,8 @@ function onServerScan(data) {
} }
// feed watchDog a `scan` type of food // feed watchDog a `scan` type of food
this.watchDog(data, {type: 'scan'}) // this.watchDog(data, {type: 'scan'})
this.emit('watchdog', { data, type: 'SCAN' })
this.emit('scan', data) this.emit('scan', data)
} }
......
/**
*
* wechaty: Wechat for Bot. and for human who talk to bot/robot
*
* Class PuppetWeb Watchdog
*
* monitor puppet
*
* Licenst: ISC
* https://github.com/zixia/wechaty
*
*/
/**************************************
*
* Class PuppetWeb
*
***************************************/
const co = require('co')
const log = require('./npmlog-env')
const Event = require('./puppet-web-event')
const Watchdog = {
onFeed
}
// feed me in time(after 1st feed), or I'll restart system
function onFeed({
data
, type = 'GARBAGE'
, timeout = 60000 // 60s default. can be override in options but be careful about the number zero(0)
} = {}) {
log.verbose('PuppetWebWatchdog', 'onFeed: %s, %d, [%s]', type, timeout, data)
switch (type) {
case 'POISON':
clearWatchDogTimer.call(this)
return
case 'GARBAGE':
case 'SCAN':
break
default:
throw new Error('unsupport type: ' + type)
}
setWatchDogTimer.call(this, timeout)
this.emit('heartbeat', data + '@' + type)
monitorScan.call(this, type)
autoSaveSession.call(this)
}
function clearWatchDogTimer() {
if (this.watchDogTimer) {
clearTimeout(this.watchDogTimer)
this.watchDogTimer = null
log.warn('PuppetWebWatchdog', 'clearWatchDogTimer() cleared')
} else {
log.warn('PuppetWebWatchdog', 'clearWatchDogTimer() nothing to clear')
}
}
function setWatchDogTimer(timeout) {
clearWatchDogTimer.call(this)
log.warn('PuppetWebWatchdog', 'setWatchDogTimer(%d)', timeout)
this.watchDogTimer = setTimeout(watchDogReset.bind(this, timeout), timeout)
// block quit, force to use quit() // this.watchDogTimer.unref() // dont block quit
}
function watchDogReset(timeout) {
log.verbose('PuppetWebWatchdog', 'watchDogReset() timeout %d', timeout)
const e = new Error('watchdog reset after ' + Math.floor(timeout/1000) + ' seconds')
this.emit('error', e)
return Event.onBrowserDead.call(this, e)
}
/**
*
* Deal with Sessions(cookies)
* save every 5 mins
*
*/
function autoSaveSession() {
const SAVE_SESSION_INTERVAL = 5 * 60 * 1000 // 5 mins
if (Date.now() - this.watchDogLastSaveSession > SAVE_SESSION_INTERVAL) {
log.verbose('PuppetWebWatchdog', 'watchDog() saveSession(%s) after %d minutes', this.profile, Math.floor(SAVE_SESSION_INTERVAL/1000/60))
this.browser.saveSession()
this.watchDogLastSaveSession = Date.now()
}
}
/**
*
* Deal with SCAN events
*
* if web browser stay at login qrcode page long time,
* sometimes the qrcode will not refresh, leave there expired.
* so we need to refresh the page after a while
*
*/
function monitorScan(type) {
log.verbose('PuppetWebWatchdog', 'monitorScan(%s)', type)
const scanTimeout = 10 * 60 * 1000 // 10 mins
if (type === 'SCAN') { // watchDog was feed a 'scan' data
this.lastScanEventTime = Date.now()
}
if (this.logined()) { // XXX: login status right?
this.lastScanEventTime = null
} else if (this.lastScanEventTime
&& Date.now() - this.lastScanEventTime > scanTimeout) {
log.warn('PuppetWebWatchdog', 'monirotScan() refresh browser for no food of type scan after %s mins', Math.floor(scanTimeout/1000/60))
// try to fix the problem
this.browser.refresh()
this.lastScanEventTime = Date.now()
}
}
module.exports = Watchdog
\ No newline at end of file
...@@ -32,7 +32,8 @@ const Server = require('./puppet-web-server') ...@@ -32,7 +32,8 @@ const Server = require('./puppet-web-server')
const Browser = require('./puppet-web-browser') const Browser = require('./puppet-web-browser')
const Bridge = require('./puppet-web-bridge') const Bridge = require('./puppet-web-bridge')
const Event = require('./puppet-web-event') const Event = require('./puppet-web-event')
const Watchdog = require('./puppet-web-watchdog')
class PuppetWeb extends Puppet { class PuppetWeb extends Puppet {
constructor({ constructor({
...@@ -54,6 +55,8 @@ class PuppetWeb extends Puppet { ...@@ -54,6 +55,8 @@ class PuppetWeb extends Puppet {
init() { init() {
log.verbose('PuppetWeb', `init() with port:${this.port}, head:${this.head}, profile:${this.profile}`) log.verbose('PuppetWeb', `init() with port:${this.port}, head:${this.head}, profile:${this.profile}`)
this.on('watchdog', Watchdog.onFeed.bind(this))
return co.call(this, function* () { return co.call(this, function* () {
yield this.initAttach(this) yield this.initAttach(this)
...@@ -68,7 +71,8 @@ class PuppetWeb extends Puppet { ...@@ -68,7 +71,8 @@ class PuppetWeb extends Puppet {
this.bridge = yield this.initBridge() this.bridge = yield this.initBridge()
log.verbose('PuppetWeb', 'initBridge() done') log.verbose('PuppetWeb', 'initBridge() done')
this.watchDog('inited') // start watchdog // this.watchDog('inited') // start watchdog
this.emit('watchdog', { data: 'inited' })
}) })
.catch(e => { // Reject .catch(e => { // Reject
log.error('PuppetWeb', 'init exception: %s', e.message) log.error('PuppetWeb', 'init exception: %s', e.message)
...@@ -83,8 +87,13 @@ class PuppetWeb extends Puppet { ...@@ -83,8 +87,13 @@ class PuppetWeb extends Puppet {
quit() { quit() {
log.verbose('PuppetWeb', 'quit()') log.verbose('PuppetWeb', 'quit()')
// this.clearWatchDogTimer()
this.emit('watchdog', {
data: 'PuppetWeb.quit()',
type: 'POISON'
})
return co.call(this, function* () { return co.call(this, function* () {
this.clearWatchDogTimer()
if (this.bridge) { if (this.bridge) {
yield this.bridge.quit().catch(e => { // fail safe yield this.bridge.quit().catch(e => { // fail safe
...@@ -194,83 +203,6 @@ class PuppetWeb extends Puppet { ...@@ -194,83 +203,6 @@ class PuppetWeb extends Puppet {
}) })
} }
clearWatchDogTimer() {
if (this.watchDogTimer) {
clearTimeout(this.watchDogTimer)
this.watchDogTimer = null
}
}
setWatchDogTimer(timeout) {
this.clearWatchDogTimer()
this.watchDogTimer = setTimeout(this.watchDogReset.bind(this, timeout), timeout)
this.watchDogTimer.unref() // dont block quit
}
// feed me in time(after 1st feed), or I'll restart system
watchDog(data, {
timeout
, type
} = {}) {
log.silly('PuppetWeb', 'watchDog(%s)', data)
timeout = timeout || 60000 // 60s default. can be override in options but be careful about the number zero(0)
type = type || 'food' // just a name
this.setWatchDogTimer(timeout)
this.emit('heartbeat', data)
/**
*
* Deal with Sessions(cookies)
*
* save every 5 mins
*
*/
const SAVE_SESSION_INTERVAL = 5 * 60 * 1000 // 5 mins
// if no lastSaveSession set(means 1st time), or timeout
if (!this.watchDogLastSaveSession) {
this.watchDogLastSaveSession = Date.now()
} else if (Date.now() - this.watchDogLastSaveSession > SAVE_SESSION_INTERVAL) {
log.verbose('PuppetWeb', 'watchDog() saveSession(%s) after %d minutes', this.profile, Math.floor(SAVE_SESSION_INTERVAL/1000/60))
this.browser.saveSession()
this.watchDogLastSaveSession = Date.now()
}
/**
*
* Deal with SCAN events
*
* if web browser stay at login qrcode page long time,
* sometimes the qrcode will not refresh, leave there expired.
* so we need to refresh the page after a while
*
*/
if (type === 'scan') { // watchDog was feed a 'scan' data
log.verbose('PuppetWeb', 'watchDog() got a food with type scan')
this.lastScanEventTime = Date.now()
}
if (this.logined()) { // XXX: login status right?
this.lastScanEventTime = null
} else if (this.lastScanEventTime) {
const scanTimeout = 10 * 60 * 1000 // 10 mins
if (Date.now() - this.lastScanEventTime > scanTimeout) {
log.warn('PuppetWeb', 'watchDog() refresh browser for no food of type scan after %s mins', Math.floor(scanTimeout/1000/60))
// try to fix the problem
this.browser.refresh()
}
}
}
watchDogReset(timeout) {
log.verbose('PuppetWeb', 'watchDogReset() timeout %d', timeout)
const e = new Error('watchdog reset after ' + Math.floor(timeout/1000) + ' seconds')
this.emit('error', e)
return Event.onBrowserDead.call(this, e)
}
self(message) { self(message) {
if (!this.userId) { if (!this.userId) {
......
...@@ -16,25 +16,27 @@ test('Puppet Web watchdog timer', function(t) { ...@@ -16,25 +16,27 @@ test('Puppet Web watchdog timer', function(t) {
t.ok(pw, 'should instantiate a PuppetWeb') t.ok(pw, 'should instantiate a PuppetWeb')
co(function* () { co(function* () {
// pw.browser = yield pw.initBrowser()
// t.ok(pw.browser, 'should init the browser')
// pw.bridge = yield pw.initBridge()
// t.ok(pw.bridge, 'should init the bridge')
// yield pw.bridge.quit().catch(e => {/* fail safe */}) yield pw.init()
// yield pw.browser.quit().catch(e => {/* fail safe */}) pw.quit()
// t.pass('should kill both browser & bridge') // yield pw.bridge.quit()
// pw.bridge = null
// yield pw.browser.quit()
// pw.browser = null
let errorCounter = 0 let errorCounter = 0
pw.once('error', e => errorCounter = 1) pw.once('error', e => errorCounter = 1)
pw.watchDog('feed_and_active_it', {timeout: 1}) pw.emit('watchdog', {
yield new Promise((resolve) => setTimeout(() => resolve(), 2)) // wait untill reset data: 'feed_and_active_it'
, timeout: 1
})
yield new Promise((resolve) => setTimeout(_ => resolve(), 10)) // wait untill reset
t.equal(errorCounter, 1, 'should get event[error] after watchdog timeout') t.equal(errorCounter, 1, 'should get event[error] after watchdog timeout')
pw.once('error', e => t.fail('waitDing() triggered watchDogReset()')) pw.once('error', e => t.fail('waitDing() triggered watchDogReset()'))
const EXPECTED_DING_DATA = 'dingdong' const EXPECTED_DING_DATA = 'dingdong'
pw.watchDog('feed to extend the dog life') pw.emit('watchdog', { data: 'feed to extend the dog life' })
const origLogLevel = log.level const origLogLevel = log.level
log.level = 'silly' log.level = 'silly'
......
...@@ -12,7 +12,7 @@ const PROFILE = 'unit-test-session.wechaty.json' ...@@ -12,7 +12,7 @@ const PROFILE = 'unit-test-session.wechaty.json'
const PuppetWeb = require('../src/puppet-web') const PuppetWeb = require('../src/puppet-web')
const Message = require('../src/message') const Message = require('../src/message')
test('PuppetWeb smoke testing', function(t) { false && test('PuppetWeb smoke testing', function(t) {
let pw = new PuppetWeb({port: PORT, head: HEAD, profile: PROFILE}) let pw = new PuppetWeb({port: PORT, head: HEAD, profile: PROFILE})
t.ok(pw, 'should instantiated a PuppetWeb') t.ok(pw, 'should instantiated a PuppetWeb')
...@@ -91,6 +91,7 @@ log.level = 'info' ...@@ -91,6 +91,7 @@ log.level = 'info'
setTimeout(_ => { setTimeout(_ => {
reject('no response timeout after ' + 2 * maxTime) reject('no response timeout after ' + 2 * maxTime)
}, 2 * maxTime) }, 2 * maxTime)
.unref()
return testDing() return testDing()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册