master.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. /*!
  2. * Cluster - Master
  3. * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var Worker = require('./worker')
  10. , EventEmitter = require('events').EventEmitter
  11. , dirname = require('path').dirname
  12. , spawn = require('child_process').spawn
  13. , utils = require('./utils')
  14. , fsBinding = process.binding('fs')
  15. , netBinding = process.binding('net')
  16. , bind = netBinding.bind
  17. , listen = netBinding.listen
  18. , socket = netBinding.socket
  19. , socketpair = netBinding.socketpair
  20. , close = netBinding.close
  21. , unlink = fsBinding.unlink
  22. , dgram = require('dgram')
  23. , tty = require('tty')
  24. , net = require('net')
  25. , fs = require('fs')
  26. , os = require('os');
  27. /**
  28. * Node binary.
  29. */
  30. var node = process.execPath;
  31. /**
  32. * Start a new `Master` with the given `server` or filename to
  33. * a node module exporting a server.
  34. *
  35. * Options:
  36. *
  37. * - `workers` Number of workers to spawn, defaults to the number of CPUs
  38. * - 'working directory` Working directory defaulting to the script's dir
  39. * - 'backlog` Connection backlog, defaulting to 128
  40. * - 'socket port` Master socket port defaulting to `8989`
  41. * - 'timeout` Worker shutdown timeout in milliseconds, defaulting to 60,000
  42. * - 'user` User id / name
  43. * - 'group` Group id / name
  44. * - `title` Master process title, defaults to "cluster master"
  45. * - `worker title` Worker process title, defaults to "cluster worker {n}"
  46. *
  47. * Events:
  48. *
  49. * - `start`. When the IPC server is prepped
  50. * - `worker`. When a worker is spawned, passing the `worker`
  51. * - `listening`. When the server is listening for connections
  52. * - `closing`. When master is shutting down
  53. * - `close`. When master has completed shutting down
  54. * - `worker killed`. When a worker has died
  55. * - `worker exception`. Worker uncaughtException. Receives the worker / exception
  56. * - `worker removed`. Worker removed via `spawn(-n)`
  57. * - `kill`. When a `signal` is being sent to all workers
  58. * - `restarting`. Restart requested by REPL or signal. Receives an object
  59. * which can be patched in order to preserve plugin state.
  60. * - `restart`. Restart complete, new master established, previous died.
  61. * Receives an object with state preserved by the `restarting` event.
  62. *
  63. * Signals:
  64. *
  65. * - `SIGINT` hard shutdown
  66. * - `SIGTERM` hard shutdown
  67. * - `SIGQUIT` graceful shutdown
  68. * - `SIGUSR2` graceful restart
  69. *
  70. * @param {net.Server|String} server
  71. * @return {Master}
  72. * @api public
  73. */
  74. var Master = module.exports = function Master(server) {
  75. var self = this;
  76. this.server = server;
  77. this.plugins = [];
  78. this.children = [];
  79. this.state = 'active';
  80. this.startup = new Date;
  81. this._killed = 0;
  82. // grab server root
  83. this.cmd = process.argv.slice(1);
  84. this.dir = dirname(this.cmd[0]);
  85. // environment
  86. this.env = process.env.NODE_ENV || 'development';
  87. // defaults
  88. this.options = {
  89. 'backlog': 128
  90. , 'working directory': this.dir
  91. , 'socket port': 8989
  92. , 'socket addr': '127.0.0.1'
  93. , 'timeout': 60000
  94. , 'restart threshold': 'development' == this.env ? 5000 : 60000
  95. , 'restart timeout': 'development' == this.env ? 5000 : 60000
  96. , 'title': 'cluster'
  97. , 'worker title': 'cluster worker'
  98. };
  99. // parent master pid
  100. this.ppid = process.env.CLUSTER_PARENT_PID
  101. ? parseInt(process.env.CLUSTER_PARENT_PID, 10)
  102. : null;
  103. // process is a worker
  104. this.isWorker = !! process.env.CLUSTER_MASTER_PID;
  105. // process is a child (worker or master replacement)
  106. this.isChild = this.isWorker || !! process.env.CLUSTER_REPLACEMENT_MASTER;
  107. // process is master
  108. this.isMaster = ! this.isWorker;
  109. // process id
  110. this.pid = process.pid;
  111. if (this.isMaster) process.env.CLUSTER_MASTER_PID = this.pid;
  112. // custom worker fds, defaults to std{out,err}
  113. this.customFds = [1, 2];
  114. // resolve server filename
  115. if (this.isWorker && 'string' == typeof this.server) {
  116. this.server = require(this.resolve(this.server));
  117. }
  118. // IPC is prepped
  119. this.on('start', function(){
  120. process.chdir(self.options['working directory']);
  121. });
  122. // spawn our workers
  123. this.on('listening', function(){
  124. self.spawn(self.options.workers);
  125. self.listening = true;
  126. });
  127. // kill children on master exception
  128. if (this.isMaster) {
  129. process.on('uncaughtException', function(err){
  130. self.kill('SIGKILL');
  131. console.error(err.stack || String(err));
  132. process.exit(1);
  133. });
  134. }
  135. };
  136. /**
  137. * Interit from `EventEmitter.prototype`.
  138. */
  139. Master.prototype.__proto__ = EventEmitter.prototype;
  140. /**
  141. * Worker is a receiver.
  142. */
  143. require('./mixins/receiver')(Master.prototype);
  144. /**
  145. * Resolve `path` relative to the server file being executed.
  146. *
  147. * @param {String} path
  148. * @return {String}
  149. * @api public
  150. */
  151. Master.prototype.resolve = function(path){
  152. return '/' == path[0]
  153. ? path
  154. : this.dir + '/' + path;
  155. };
  156. /**
  157. * Return `true` when the environment set by `Master#in()`
  158. * matches __NODE_ENV__.
  159. *
  160. * @return {Boolean}
  161. * @api private
  162. */
  163. Master.prototype.__defineGetter__('environmentMatches', function(){
  164. if (this._env)
  165. return this.env == this._env || 'all' == this._env;
  166. return true;
  167. });
  168. /**
  169. * Invoke masters's `method` with worker `id`. (called from Worker)
  170. *
  171. * @param {Number} id
  172. * @param {String} method
  173. * @param {...} args
  174. * @api private
  175. */
  176. Master.prototype.call = function(id, method){
  177. this.sock = this.sock || dgram.createSocket('udp4');
  178. var msg = new Buffer(utils.frame({
  179. args: utils.toArray(arguments, 2)
  180. , method: method
  181. , id: id
  182. }));
  183. this.sock.send(
  184. msg
  185. , 0
  186. , msg.length
  187. , this.options['socket port']
  188. , this.options['socket addr']);
  189. };
  190. /**
  191. * Perform setup tasks then invoke `fn()` when present.
  192. *
  193. * @param {Function} fn
  194. * @return {Master} for chaining
  195. * @api public
  196. */
  197. Master.prototype.start = function(fn){
  198. var self = this;
  199. // deferred title
  200. process.title = this.options.title;
  201. // prevent listen
  202. if (this.preventDefault) return this;
  203. // env match
  204. if (this.environmentMatches) {
  205. // worker process
  206. if (this.isWorker) {
  207. this.worker = new Worker(this);
  208. this.worker.start();
  209. // master process
  210. } else if (fn) {
  211. fn();
  212. // standalone
  213. } else {
  214. this.on('start', function(){ self.emit('listening'); });
  215. if (this.isChild) this.acceptFd();
  216. this.setupIPC();
  217. }
  218. }
  219. return this;
  220. };
  221. /**
  222. * Defer `http.Server#listen()` call.
  223. *
  224. * @param {Number|String} port or unix domain socket path
  225. * @param {String|Function} host or callback
  226. * @param {Function} callback
  227. * @return {Master} for chaining
  228. * @api public
  229. */
  230. Master.prototype.listen = function(port, host, callback){
  231. var self = this;
  232. if (!this.environmentMatches) return this;
  233. if ('function' == typeof host) callback = host, host = null;
  234. this.port = port;
  235. this.host = host;
  236. this.callback = callback;
  237. return this.start(function(){
  238. self.on('start', function(){
  239. self.startListening(!self.isChild);
  240. });
  241. if (self.isChild) {
  242. self.acceptFd();
  243. } else {
  244. self.createSocket(function(err, fd){
  245. if (err) throw err;
  246. self.fd = fd;
  247. self.setupIPC();
  248. });
  249. }
  250. });
  251. };
  252. /**
  253. * Create / return IPC socket.
  254. *
  255. * @api private
  256. */
  257. Master.prototype.IPCSocket = function(){
  258. var self = this;
  259. if (this._sock) return this._sock;
  260. this._sock = dgram.createSocket('udp4');
  261. this._sock.on('message', function(msg, info){
  262. try {
  263. msg = JSON.parse(msg.toString('ascii'));
  264. self.invoke(msg.method, msg.args, self.children[msg.id]);
  265. } catch (err) {
  266. console.error(err.stack || String(err));
  267. }
  268. });
  269. return this._sock;
  270. };
  271. /**
  272. * Setup IPC.
  273. *
  274. * @api private
  275. */
  276. Master.prototype.setupIPC = function(){
  277. var self = this;
  278. // signal handlers
  279. this.registerSignalHandlers();
  280. // Default worker to the # of cpus
  281. this.defaultWorkers();
  282. // udp server for IPC
  283. this.IPCSocket().on('listening', function(){
  284. process.nextTick(function(){
  285. self.emit('start');
  286. });
  287. });
  288. // bind
  289. this.IPCSocket().bind(
  290. this.options['socket port']
  291. , this.options['socket addr']);
  292. };
  293. /**
  294. * Conditionally perform the following action, if
  295. * __NODE_ENV__ matches `env`.
  296. *
  297. * Examples:
  298. *
  299. * cluster(server)
  300. * .in('development').use(cluster.debug())
  301. * .in('development').listen(3000)
  302. * .in('production').listen(80);
  303. *
  304. * @param {String} env
  305. * @return {Master} self or stubs
  306. * @api public
  307. */
  308. Master.prototype.in = function(env){
  309. this._env = env;
  310. return this;
  311. };
  312. /**
  313. * Set option `key` to `val`.
  314. *
  315. * @param {String} key
  316. * @param {Mixed} val
  317. * @return {Master} for chaining
  318. * @api public
  319. */
  320. Master.prototype.set = function(key, val){
  321. if (this.environmentMatches) this.options[key] = val;
  322. return this;
  323. };
  324. /**
  325. * Invoke `fn(master)`.
  326. *
  327. * @param {Function} fn
  328. * @api public
  329. */
  330. Master.prototype.do = function(fn){
  331. if (this.environmentMatches) fn.call(this, this);
  332. return this;
  333. };
  334. /**
  335. * Check if `option` has been set.
  336. *
  337. * @param {String} option
  338. * @return {Boolean}
  339. * @api public
  340. */
  341. Master.prototype.has = function(option){
  342. return !! this.options[option];
  343. };
  344. /**
  345. * Use the given `plugin`.
  346. *
  347. * @param {Function} plugin
  348. * @return {Master} for chaining
  349. * @api public
  350. */
  351. Master.prototype.use = function(plugin){
  352. if (this.environmentMatches) {
  353. this.plugins.push(plugin);
  354. if (this.isWorker) {
  355. plugin.enableInWorker && plugin(this);
  356. } else {
  357. plugin(this);
  358. }
  359. }
  360. return this;
  361. };
  362. /**
  363. * Create listening socket and callback `fn(err, fd)`.
  364. *
  365. * @return {Function} fn
  366. * @api private
  367. */
  368. Master.prototype.createSocket = function(fn){
  369. var self = this
  370. , ipv;
  371. // explicit host
  372. if (this.host) {
  373. // ip
  374. if (ipv = net.isIP(this.host)) {
  375. fn(null, socket('tcp' + ipv));
  376. // lookup
  377. } else {
  378. require('dns').lookup(this.host, function(err, ip, ipv){
  379. if (err) return fn(err);
  380. self.host = ip;
  381. fn(null, socket('tcp' + ipv));
  382. });
  383. }
  384. // local socket
  385. } else if ('string' == typeof this.port) {
  386. fn(null, socket('unix'));
  387. // only port
  388. } else if ('number' == typeof this.port) {
  389. fn(null, socket('tcp4'));
  390. }
  391. };
  392. /**
  393. * Register signal handlers.
  394. *
  395. * @api private
  396. */
  397. Master.prototype.registerSignalHandlers = function(){
  398. var self = this;
  399. process.on('SIGINT', this.destroy.bind(this));
  400. process.on('SIGTERM', this.destroy.bind(this));
  401. process.on('SIGQUIT', this.close.bind(this));
  402. process.on('SIGUSR2', this.attemptRestart.bind(this));
  403. process.on('SIGCHLD', this.maintainWorkerCount.bind(this));
  404. };
  405. /**
  406. * Default workers to the number of cpus available.
  407. *
  408. * @api private
  409. */
  410. Master.prototype.defaultWorkers = function(){
  411. if (!this.has('workers')) {
  412. this.set('workers', os
  413. ? os.cpus().length
  414. : 1);
  415. }
  416. };
  417. /**
  418. * Restart workers only, sending `signal` defaulting
  419. * to __SIGQUIT__.
  420. *
  421. * @param {Type} name
  422. * @return {Type}
  423. * @api public
  424. */
  425. Master.prototype.restartWorkers = function(signal){
  426. this.kill(signal || 'SIGQUIT');
  427. };
  428. /**
  429. * Maintain worker count, re-spawning if necessary.
  430. *
  431. * @api private
  432. */
  433. Master.prototype.maintainWorkerCount = function(){
  434. this.children.forEach(function(worker){
  435. var pid = worker.proc.pid;
  436. if (!pid) this.workerKilled(worker);
  437. }, this);
  438. };
  439. /**
  440. * Remove `n` workers with `signal`
  441. * defaulting to __SIGQUIT__.
  442. *
  443. * @param {Number} n
  444. * @param {String} signal
  445. * @api public
  446. */
  447. Master.prototype.remove = function(n, signal){
  448. if (!arguments.length) n = 1;
  449. var len = this.children.length
  450. , worker;
  451. // cap at worker len
  452. if (n > len) n = len;
  453. // remove the workers
  454. while (n--) {
  455. worker = this.children.pop();
  456. worker.proc.kill(signal || 'SIGQUIT');
  457. this.emit('worker removed', worker);
  458. this.removeWorker(worker.id);
  459. }
  460. };
  461. /**
  462. * Remove worker `id`.
  463. *
  464. * @param {Number} id
  465. * @api public
  466. */
  467. Master.prototype.removeWorker = function(id){
  468. var worker = this.children[id];
  469. if (!worker) return;
  470. if (worker.fds) {
  471. close(worker.fds[0]);
  472. close(worker.fds[1]);
  473. }
  474. delete this.children[id];
  475. };
  476. /**
  477. * Spawn `n` workers.
  478. *
  479. * @param {Number} n
  480. * @api public
  481. */
  482. Master.prototype.spawn = function(n){
  483. if (!arguments.length) n = 1;
  484. while (n--) this.spawnWorker();
  485. };
  486. /**
  487. * Spawn a worker with optional `id`.
  488. *
  489. * @param {Number} id
  490. * @return {Worker}
  491. * @api private
  492. */
  493. Master.prototype.spawnWorker = function(id){
  494. var worker;
  495. // id given
  496. if ('number' == typeof id) {
  497. worker = new Worker(this).spawn(id)
  498. this.children[id] = worker;
  499. worker.id = id;
  500. // generate an id
  501. } else {
  502. worker = new Worker(this).spawn(this.children.length);
  503. this.children.push(worker);
  504. }
  505. var obj = {
  506. method: 'connect'
  507. , args: [worker.id, this.options]
  508. };
  509. worker.sock.write(utils.frame(obj), 'ascii', this.fd);
  510. // emit
  511. this.emit('worker', worker);
  512. return worker;
  513. };
  514. /**
  515. * Graceful shutdown, wait for all workers
  516. * to reply before exiting.
  517. *
  518. * @api public
  519. */
  520. Master.prototype.close = function(){
  521. this.state = 'graceful shutdown';
  522. this.emit('closing');
  523. this.kill('SIGQUIT');
  524. this.pendingDeaths = this.children.length;
  525. };
  526. /**
  527. * Hard shutdwn, immediately kill all workers.
  528. *
  529. * @api public
  530. */
  531. Master.prototype.destroy = function(){
  532. this.state = 'hard shutdown';
  533. this.emit('closing');
  534. this.kill('SIGKILL');
  535. this._destroy();
  536. };
  537. /**
  538. * Attempt restart, while respecting the `restart threshold`
  539. * setting, to help prevent recursive restarts.
  540. *
  541. * @param {String} sig
  542. * @api private
  543. */
  544. Master.prototype.attemptRestart = function(sig){
  545. var uptime = new Date - this.startup
  546. , threshold = this.options['restart threshold']
  547. , timeout = this.options['restart timeout'];
  548. if (this.__restarting) return;
  549. if (uptime < threshold) {
  550. this.__restarting = true;
  551. this.emit('cyclic restart');
  552. setTimeout(function(self){
  553. self.restart(sig);
  554. }, timeout, this);
  555. } else {
  556. this.restart(sig);
  557. }
  558. };
  559. /**
  560. * Restart all workers, by sending __SIGQUIT__
  561. * or `sig` to them, enabling master to re-spawn.
  562. *
  563. * @param {String} sig
  564. * @return {ChildProcess} replacement master process
  565. * @api public
  566. */
  567. Master.prototype.restart = function(sig){
  568. var data = {}
  569. , proc = this.spawnMaster();
  570. // pass object to plugins, allowing them
  571. // to patch it, and utilize the data in
  572. // the new Master
  573. this.emit('restarting', data);
  574. proc.sock.write(utils.frame({
  575. method: 'connectMaster'
  576. , args: [sig || 'SIGQUIT']
  577. }), 'ascii', this.fd);
  578. this.on('close', function(){
  579. proc.sock.write(utils.frame({
  580. method: 'masterKilled'
  581. , args: [data]
  582. }), 'ascii');
  583. });
  584. return proc;
  585. };
  586. /**
  587. * Spawn a new master process.
  588. *
  589. * @return {ChildProcess}
  590. * @api private
  591. */
  592. Master.prototype.spawnMaster = function(){
  593. var fds = socketpair()
  594. , customFds = [fds[0], 1, 2]
  595. , env = {};
  596. // merge current env
  597. for (var key in process.env) {
  598. env[key] = process.env[key];
  599. }
  600. delete env.CLUSTER_MASTER_PID;
  601. env.CLUSTER_REPLACEMENT_MASTER = 1;
  602. env.CLUSTER_PARENT_PID = this.pid;
  603. // spawn new master process
  604. var proc = spawn(node, this.cmd, {
  605. customFds: customFds
  606. , env: env
  607. });
  608. // unix domain socket for ICP + fd passing
  609. proc.sock = new net.Socket(fds[1], 'unix');
  610. return proc;
  611. };
  612. /**
  613. * Master replacement connected.
  614. *
  615. * @param {String} sig
  616. * @api private
  617. */
  618. Master.prototype.connectMaster = function(sig){
  619. var self = this;
  620. function kill(){
  621. process.kill(self.ppid, sig);
  622. }
  623. if (this.listening) return kill();
  624. this.on('listening', kill);
  625. };
  626. /**
  627. * Original master has died aka 'retired',
  628. * we now fire the 'restart' event.
  629. *
  630. * @param {Object} data
  631. * @api private
  632. */
  633. Master.prototype.masterKilled = function(data){
  634. this.emit('restart', data);
  635. };
  636. /**
  637. * Accept fd from parent master, then `setupIPC()`.
  638. *
  639. * @api private
  640. */
  641. Master.prototype.acceptFd = function(){
  642. var self = this
  643. , stdin = new net.Socket(0, 'unix');
  644. // set fd and start master
  645. stdin.setEncoding('ascii');
  646. stdin.on('fd', function(fd){
  647. self.fd = fd;
  648. self.setupIPC();
  649. });
  650. // frame commands from the parent master
  651. stdin.on('data', this.frame.bind(this));
  652. stdin.resume();
  653. };
  654. /**
  655. * Close servers and emit 'close' before exiting.
  656. *
  657. * @api private
  658. */
  659. Master.prototype._destroy = function(){
  660. this.IPCSocket().close();
  661. if (this.fd) close(this.fd);
  662. this.emit('close');
  663. process.nextTick(process.exit.bind(process));
  664. };
  665. /**
  666. * Worker is connected.
  667. *
  668. * @param {Worker} worker
  669. * @api private
  670. */
  671. Master.prototype.connect = function(worker){
  672. this.emit('worker connected', worker);
  673. };
  674. /**
  675. * Start listening, when `shouldBind` is `true` the socket
  676. * will be bound, and will start listening for connections.
  677. *
  678. * @param {Boolean} shouldBind
  679. * @api private
  680. */
  681. Master.prototype.startListening = function(shouldBind){
  682. var self = this;
  683. // remove unix domain socket
  684. if ('string' == typeof this.port && shouldBind) {
  685. fs.unlink(this.port, function(err){
  686. if (err && 'ENOENT' != err.code) throw err;
  687. startListening();
  688. });
  689. } else {
  690. startListening();
  691. }
  692. // bind / listen
  693. function startListening() {
  694. if (shouldBind) {
  695. try {
  696. bind(self.fd, self.port, self.host);
  697. listen(self.fd, self.options.backlog);
  698. } catch(e) {
  699. self.kill('SIGKILL');
  700. throw e;
  701. }
  702. }
  703. self.callback && self.callback();
  704. self.emit('listening');
  705. }
  706. };
  707. /**
  708. * The given `worker` has been killed.
  709. * Emit the "worker killed" event, remove
  710. * the worker, and re-spawn depending on
  711. * the master state.
  712. *
  713. * @api private
  714. */
  715. Master.prototype.workerKilled = function(worker){
  716. // if we have many failing workers at boot
  717. // then we likely have a serious issue.
  718. if (new Date - this.startup < 20000) {
  719. if (++this._killed == 20) {
  720. console.error('');
  721. console.error('Cluster detected over 20 worker deaths in the first');
  722. console.error('20 seconds of life, there is most likely');
  723. console.error('a serious issue with your server.');
  724. console.error('');
  725. console.error('aborting.');
  726. console.error('');
  727. process.exit(1);
  728. }
  729. }
  730. // emit event
  731. this.emit('worker killed', worker);
  732. // always remove worker
  733. this.removeWorker(worker.id);
  734. // state specifics
  735. switch (this.state) {
  736. case 'hard shutdown':
  737. break;
  738. case 'graceful shutdown':
  739. --this.pendingDeaths || this._destroy();
  740. break;
  741. default:
  742. this.spawnWorker(worker.id);
  743. }
  744. };
  745. /**
  746. * `worker` received exception `err`.
  747. *
  748. * @api private
  749. */
  750. Master.prototype.workerException = function(worker, err){
  751. this.emit('worker exception', worker, err);
  752. };
  753. /**
  754. * Received worker timeout.
  755. *
  756. * @api private
  757. */
  758. Master.prototype.workerTimeout = function(worker, timeout){
  759. this.emit('worker timeout', worker, timeout);
  760. };
  761. /**
  762. * Worker waiting on `connections` to close.
  763. *
  764. * @api private
  765. */
  766. Master.prototype.workerWaiting = function(worker, connections){
  767. this.emit('worker waiting', worker, connections);
  768. };
  769. /**
  770. * Send `sig` to all worker processes, defaults to __SIGTERM__.
  771. *
  772. * @param {String} sig
  773. * @api public
  774. */
  775. Master.prototype.kill = function(sig){
  776. var self = this;
  777. this.emit('kill', sig);
  778. this.children.forEach(function(worker){
  779. worker.proc.kill(sig);
  780. });
  781. };