-
Notifications
You must be signed in to change notification settings - Fork 27
/
index.js
117 lines (103 loc) · 3.13 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
var cluster = require('cluster')
var os = require('os')
var shuttingDown = false
var KILL_TIMEOUT = 1000
var LOG = false
function killTimeout(worker, timeout) {
forky.log('setting kill timeout of', timeout, 'for worker', worker.id)
var tid = setTimeout(function() {
forky.log('worker', worker.id, 'did not shutdown after timeout', timeout, 'killing')
worker.destroy()
}, timeout)
worker.once('exit', function() {
forky.log('worker', worker.id, 'died. clear kill timeout')
clearTimeout(tid)
})
}
//fork a new worker
function forkWorker() {
if (shuttingDown) return
var worker = cluster.fork()
forky.log('forked worker', worker.id)
//set up a listener for the disconnect message
//a worker can send this by calling `forky.disconnect([timeout])`
worker.on('message', function(msg) {
if (msg.action != 'disconnect') return
forkWorker()
worker.disconnect()
if (!msg.timeout) return
killTimeout(worker, msg.timeout)
})
worker.once('disconnect', function() {
forky.log('worker', worker.id, ' disconnected.', 'exitedAfterDisconnect', worker.exitedAfterDisconnect)
if (worker.exitedAfterDisconnect) return
forkWorker()
//set short kill timeout for unexpected worker shutdown
killTimeout(worker, KILL_TIMEOUT)
})
}
var forky = (module.exports = function(options, workerCount, cb) {
var path
if (typeof options === 'string') {
// this is here for backwards compatibility to 0.1.2, remove this when we hit 1.0.0
path = options
if (typeof workerCount == 'function') {
cb = workerCount
workerCount = undefined
}
} else {
path = options.path
workerCount = options.workers
cb = options.callback
if (options.enable_logging !== undefined) {
LOG = options.enable_logging
}
if (options.kill_timeout !== undefined) {
KILL_TIMEOUT = options.kill_timeout
}
if (options.scheduling_policy !== undefined) {
cluster.schedulingPolicy = options.scheduling_policy;
}
}
if (undefined === workerCount) {
workerCount = os.cpus().length
}
cluster.setupMaster({
exec: path,
})
forky.log('starting', workerCount, 'workers')
for (var i = 0; i < workerCount; i++) {
forkWorker()
}
var listeningWorkers = 0
cluster.on('listening', function(worker) {
if (++listeningWorkers == workerCount) {
cb ? cb(null, cluster) : function() {}
}
})
})
//call this from a worker to disconnect the worker
//forky will automatically spawn a new worker in its place
forky.disconnect = function(timeout) {
if (!cluster.isWorker) {
throw new Error('You are not a worker')
}
var worker = cluster.worker
if (worker.state == 'disconnecting') return
worker.state = 'disconnecting'
worker.disconnectTimeout = timeout
forky.log('disconnecting worker', worker.id)
if (timeout) {
worker.send({ action: 'disconnect', timeout: timeout })
} else {
worker.send({ action: 'disconnect' })
}
}
//this is a no-op but you can override it if you
//want some detailed log messages about what
//forky is doing with your workers
forky.log = function() {
if (LOG) {
console.log.apply(console, arguments)
}
}