repl.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*!
  2. * Cluster - repl
  3. * Copyright (c) 2011 LearnBoost <dev@learnboost.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var net = require('net')
  10. , repl = require('repl');
  11. /**
  12. * Enable REPL with all arguments passed to `net.Server#listen()`.
  13. *
  14. * Examples:
  15. *
  16. * cluster(server)
  17. * .use(cluster.stats())
  18. * .use(cluster.repl('/var/run/cluster'))
  19. * .listen();
  20. *
  21. * In the terminal:
  22. *
  23. * $ sudo telnet /var/run/cluster
  24. *
  25. * @return {Function}
  26. * @api public
  27. */
  28. exports = module.exports = function(){
  29. var args = arguments;
  30. if (!args.length) throw new Error('repl() plugin requires port/host or path');
  31. return function(master){
  32. var server
  33. , sockets = [];
  34. // start repl
  35. function start(){
  36. // TCP or unix-domain socket repl
  37. server = net.createServer(function(sock){
  38. sockets.push(sock);
  39. var ctx = repl.start('cluster> ', sock).context;
  40. master.emit('repl socket', sock);
  41. // augment socket to provide some formatting methods
  42. sock.title = function(str){ this.write('\n \033[36m' + str + '\033[0m\n'); }
  43. sock.row = function(key, val){ this.write(' \033[90m' + key + ':\033[0m ' + val + '\n'); }
  44. // merge commands into context
  45. // executing in context of master
  46. Object.keys(exports).forEach(function(cmd){
  47. ctx[cmd] = function(){
  48. var args = Array.prototype.slice.call(arguments);
  49. args.unshift(master, sock);
  50. return exports[cmd].apply(master, args);
  51. };
  52. });
  53. });
  54. // Apply all arguments given
  55. server.listen.apply(server, args);
  56. }
  57. // initial master starts immediately
  58. // replacements starts when the previous
  59. // has closed
  60. master.on(master.isChild
  61. ? 'restart'
  62. : 'start', start);
  63. // restart notification
  64. master.on('restarting', function(){
  65. sockets.forEach(function(sock){
  66. if (sock.fd) {
  67. sock.write('\n\033[33mrestarting\033[0m - closing connection soon\n');
  68. }
  69. });
  70. });
  71. // close
  72. master.on('close', function(){
  73. sockets.forEach(function(sock){
  74. sock.fd && sock.end();
  75. });
  76. if (server && server.fd) server.close();
  77. });
  78. }
  79. };
  80. /**
  81. * Define function `name`, with the given callback
  82. * `fn(master, sock, ...)` and `description`.
  83. *
  84. * @param {String} name
  85. * @param {Function} fn
  86. * @param {String} desc
  87. * @return {Object} exports for chaining
  88. * @api public
  89. */
  90. var define = exports.define = function(name, fn, desc){
  91. (exports[name] = fn).description = desc;
  92. return exports;
  93. };
  94. /**
  95. * Display commmand help.
  96. */
  97. define('help', function(master, sock){
  98. sock.title('Commands');
  99. Object.keys(exports).forEach(function(cmd){
  100. if ('define' == cmd) return;
  101. var fn = exports[cmd]
  102. , params = fn.toString().match(/^function +\((.*?)\)/)[1]
  103. , params = params.split(/ *, */).slice(2);
  104. sock.row(
  105. cmd + '(' + params.join(', ') + ')'
  106. , fn.description);
  107. });
  108. sock.write('\n');
  109. }, 'Display help information');
  110. /**
  111. * Spawn `n` additional workers with the given `signal`.
  112. */
  113. define('spawn', function(master, sock, n, signal){
  114. n = n || 1;
  115. if (n < 0) {
  116. n = Math.abs(n);
  117. sock.write('removing ' + n + ' worker' + (n > 1 ? 's' : '')
  118. + ' with ' + (signal || 'SIGQUIT') + '\n');
  119. master.remove(n, signal);
  120. } else {
  121. sock.write('spawning ' + n + ' worker' + (n > 1 ? 's' : '') + '\n');
  122. master.spawn(n);
  123. }
  124. }, 'Spawn one or more additional workers, or remove one or more');
  125. /**
  126. * Output process ids.
  127. */
  128. define('pids', function(master, sock){
  129. sock.title('pids');
  130. sock.row('master', process.pid);
  131. master.children.forEach(function(worker){
  132. sock.row('worker #' + worker.id, worker.proc.pid);
  133. });
  134. sock.write('\n');
  135. }, 'Output process ids');
  136. /**
  137. * Kill the given worker by `id` and `signal`.
  138. */
  139. define('kill', function(master, sock, id, signal){
  140. var worker = master.children[id];
  141. if (worker) {
  142. worker.proc.kill(signal);
  143. sock.write('sent \033[36m' + (signal || 'SIGTERM') + '\033[0m to worker #' + id + '\n');
  144. } else {
  145. sock.write('invalid worker id\n');
  146. }
  147. }, 'Send signal or SIGTERM to the given worker');
  148. /**
  149. * Gracefully shutdown.
  150. */
  151. define('shutdown', function(master, sock){
  152. master.close();
  153. }, 'Gracefully shutdown server');
  154. /**
  155. * Hard shutdown.
  156. */
  157. define('stop', function(master, sock){
  158. master.destroy();
  159. }, 'Hard shutdown');
  160. /**
  161. * Gracefully restart all workers.
  162. */
  163. define('restart', function(master, sock){
  164. master.restart();
  165. }, 'Gracefully restart all workers');