console.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /*!
  2. * Pomelo -- consoleModule serverStop stop/kill
  3. * Copyright(c) 2012 fantasyni <fantasyni@163.com>
  4. * MIT Licensed
  5. */
  6. var logger = require('pomelo-logger').getLogger('pomelo', __filename);
  7. var countDownLatch = require('../util/countDownLatch');
  8. var utils = require('../util/utils');
  9. var Constants = require('../util/constants');
  10. var starter = require('../master/starter');
  11. var exec = require('child_process').exec;
  12. module.exports = function(opts) {
  13. return new Module(opts);
  14. };
  15. module.exports.moduleId = '__console__';
  16. var Module = function(opts) {
  17. opts = opts || {};
  18. this.app = opts.app;
  19. this.starter = opts.starter;
  20. };
  21. Module.prototype.monitorHandler = function(agent, msg, cb) {
  22. var serverId = agent.id;
  23. switch(msg.signal) {
  24. case 'stop':
  25. if(agent.type === Constants.RESERVED.MASTER) {
  26. return;
  27. }
  28. this.app.stop(true);
  29. break;
  30. case 'list':
  31. var serverType = agent.type;
  32. var pid = process.pid;
  33. var heapUsed = (process.memoryUsage().heapUsed/(1024 * 1024)).toFixed(2);
  34. var rss = (process.memoryUsage().rss/(1024 * 1024)).toFixed(2);
  35. var heapTotal = (process.memoryUsage().heapTotal/(1024 * 1024)).toFixed(2);
  36. var uptime = (process.uptime()/60).toFixed(2);
  37. utils.invokeCallback(cb, {
  38. serverId: serverId,
  39. body: {serverId:serverId, serverType: serverType, pid:pid, rss: rss, heapTotal: heapTotal, heapUsed:heapUsed, uptime:uptime}
  40. });
  41. break;
  42. case 'kill':
  43. utils.invokeCallback(cb, serverId);
  44. if (agent.type !== 'master') {
  45. setTimeout(function() {
  46. process.exit(-1);
  47. }, Constants.TIME.TIME_WAIT_MONITOR_KILL);
  48. }
  49. break;
  50. case 'addCron':
  51. this.app.addCrons([msg.cron]);
  52. break;
  53. case 'removeCron':
  54. this.app.removeCrons([msg.cron]);
  55. break;
  56. case 'blacklist':
  57. if(this.app.isFrontend()) {
  58. var connector = this.app.components.__connector__;
  59. connector.blacklist = connector.blacklist.concat(msg.blacklist);
  60. }
  61. break;
  62. case 'restart':
  63. if(agent.type === Constants.RESERVED.MASTER) {
  64. return;
  65. }
  66. var self = this;
  67. var server = this.app.get(Constants.RESERVED.CURRENT_SERVER);
  68. utils.invokeCallback(cb, server);
  69. process.nextTick(function() {
  70. self.app.stop(true);
  71. });
  72. break;
  73. default:
  74. logger.error('receive error signal: %j', msg);
  75. break;
  76. }
  77. };
  78. Module.prototype.clientHandler = function(agent, msg, cb) {
  79. var app = this.app;
  80. switch(msg.signal) {
  81. case 'kill':
  82. kill(app, agent, msg, cb);
  83. break;
  84. case 'stop':
  85. stop(app, agent, msg, cb);
  86. break;
  87. case 'list':
  88. list(agent, msg, cb);
  89. break;
  90. case 'add':
  91. add(app, msg, cb);
  92. break;
  93. case 'addCron':
  94. addCron(app, agent, msg, cb);
  95. break;
  96. case 'removeCron':
  97. removeCron(app, agent, msg, cb);
  98. break;
  99. case 'blacklist':
  100. blacklist(agent, msg, cb);
  101. break;
  102. case 'restart':
  103. restart(app, agent, msg, cb);
  104. break;
  105. default:
  106. utils.invokeCallback(cb, new Error('The command cannot be recognized, please check.'), null);
  107. break;
  108. }
  109. };
  110. var kill = function(app, agent, msg, cb) {
  111. var sid, record;
  112. var serverIds = [];
  113. var count = utils.size(agent.idMap);
  114. var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_MASTER_KILL}, function(isTimeout) {
  115. if (!isTimeout) {
  116. utils.invokeCallback(cb, null, {code: 'ok'});
  117. } else {
  118. utils.invokeCallback(cb, null, {code: 'remained', serverIds: serverIds});
  119. }
  120. setTimeout(function() {
  121. process.exit(-1);
  122. }, Constants.TIME.TIME_WAIT_MONITOR_KILL);
  123. });
  124. var agentRequestCallback = function(msg) {
  125. for (var i = 0; i < serverIds.length; ++i) {
  126. if (serverIds[i] === msg) {
  127. serverIds.splice(i,1);
  128. latch.done();
  129. break;
  130. }
  131. }
  132. };
  133. for(sid in agent.idMap) {
  134. record = agent.idMap[sid];
  135. serverIds.push(record.id);
  136. agent.request(record.id, module.exports.moduleId, { signal: msg.signal }, agentRequestCallback);
  137. }
  138. };
  139. var stop = function(app, agent, msg, cb) {
  140. var serverIds = msg.ids;
  141. if(!!serverIds.length) {
  142. var servers = app.getServers();
  143. app.set(Constants.RESERVED.STOP_SERVERS, serverIds);
  144. for(var i=0; i<serverIds.length; i++) {
  145. var serverId = serverIds[i];
  146. if(!servers[serverId]) {
  147. utils.invokeCallback(cb, new Error('Cannot find the server to stop.'), null);
  148. } else {
  149. agent.notifyById(serverId, module.exports.moduleId, { signal: msg.signal });
  150. }
  151. }
  152. utils.invokeCallback(cb, null, { status: "part" });
  153. } else {
  154. agent.notifyAll(module.exports.moduleId, { signal: msg.signal });
  155. setTimeout(function() {
  156. app.stop(true);
  157. utils.invokeCallback(cb, null, { status: "all" });
  158. }, Constants.TIME.TIME_WAIT_STOP);
  159. }
  160. };
  161. var restart = function(app, agent, msg, cb) {
  162. var successFlag;
  163. var successIds = [];
  164. var serverIds = msg.ids;
  165. var type = msg.type;
  166. var servers;
  167. if(!serverIds.length && !!type) {
  168. servers = app.getServersByType(type);
  169. if(!servers) {
  170. utils.invokeCallback(cb, new Error('restart servers with unknown server type: ' + type));
  171. return;
  172. }
  173. for(var i=0; i<servers.length; i++) {
  174. serverIds.push(servers[i].id);
  175. }
  176. } else if(!serverIds.length) {
  177. servers = app.getServers();
  178. for(var key in servers) {
  179. serverIds.push(key);
  180. }
  181. }
  182. var count = serverIds.length;
  183. var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function() {
  184. if(!successFlag) {
  185. utils.invokeCallback(cb, new Error('all servers start failed.'));
  186. return;
  187. }
  188. utils.invokeCallback(cb, null, utils.arrayDiff(serverIds, successIds));
  189. });
  190. var request = function(id) {
  191. return (function() {
  192. agent.request(id, module.exports.moduleId, { signal: msg.signal }, function(msg) {
  193. if(!utils.size(msg)) {
  194. latch.done();
  195. return;
  196. }
  197. setTimeout(function() {
  198. runServer(app, msg, function(err, status) {
  199. if(!!err) {
  200. logger.error('restart ' + id + ' failed.');
  201. } else {
  202. successIds.push(id);
  203. successFlag = true;
  204. }
  205. latch.done();
  206. });
  207. }, Constants.TIME.TIME_WAIT_RESTART);
  208. });
  209. })();
  210. };
  211. for(var j=0; j<serverIds.length; j++) {
  212. request(serverIds[j]);
  213. }
  214. };
  215. var list = function(agent, msg, cb) {
  216. var sid, record;
  217. var serverInfo = {};
  218. var count = utils.size(agent.idMap);
  219. var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function() {
  220. utils.invokeCallback(cb, null, { msg: serverInfo });
  221. });
  222. var callback = function(msg) {
  223. serverInfo[msg.serverId] = msg.body;
  224. latch.done();
  225. };
  226. for(sid in agent.idMap) {
  227. record = agent.idMap[sid];
  228. agent.request(record.id, module.exports.moduleId, { signal: msg.signal }, callback);
  229. }
  230. };
  231. var add = function(app, msg, cb) {
  232. if(checkCluster(msg)) {
  233. startCluster(app, msg, cb);
  234. } else {
  235. startServer(app, msg, cb);
  236. }
  237. reset(ServerInfo);
  238. };
  239. var addCron = function(app, agent, msg, cb) {
  240. var cron = parseArgs(msg, CronInfo, cb);
  241. sendCronInfo(cron, agent, msg, CronInfo, cb);
  242. };
  243. var removeCron = function(app, agent, msg, cb) {
  244. var cron = parseArgs(msg, RemoveCron, cb);
  245. sendCronInfo(cron, agent, msg, RemoveCron, cb);
  246. };
  247. var blacklist = function(agent, msg, cb) {
  248. var ips = msg.args;
  249. for(var i=0; i<ips.length; i++) {
  250. if(!(new RegExp(/(\d+)\.(\d+)\.(\d+)\.(\d+)/g).test(ips[i]))) {
  251. utils.invokeCallback(cb, new Error('blacklist ip: ' + ips[i] + ' is error format.'), null);
  252. return;
  253. }
  254. }
  255. agent.notifyAll(module.exports.moduleId, { signal: msg.signal, blacklist: msg.args });
  256. process.nextTick(function() {
  257. cb(null, { status: "ok" });
  258. });
  259. };
  260. var checkPort = function(server, cb) {
  261. if (!server.port && !server.clientPort) {
  262. utils.invokeCallback(cb, 'leisure');
  263. return;
  264. }
  265. var p = server.port || server.clientPort;
  266. var host = server.host;
  267. var cmd = 'netstat -tln | grep ';
  268. if (!utils.isLocal(host)) {
  269. cmd = 'ssh ' + host + ' ' + cmd;
  270. }
  271. exec(cmd + p, function(err, stdout, stderr) {
  272. if (stdout || stderr) {
  273. utils.invokeCallback(cb, 'busy');
  274. } else {
  275. p = server.clientPort;
  276. exec(cmd + p, function(err, stdout, stderr) {
  277. if (stdout || stderr) {
  278. utils.invokeCallback(cb, 'busy');
  279. } else {
  280. utils.invokeCallback(cb, 'leisure');
  281. }
  282. });
  283. }
  284. });
  285. };
  286. var parseArgs = function(msg, info, cb) {
  287. var rs = {};
  288. var args = msg.args;
  289. for(var i =0; i<args.length; i++) {
  290. if(args[i].indexOf('=') < 0) {
  291. cb(new Error('Error server parameters format.'), null);
  292. return;
  293. }
  294. var pairs = args[i].split('=');
  295. var key = pairs[0];
  296. if(!!info[key]) {
  297. info[key] = 1;
  298. }
  299. rs[pairs[0]] = pairs[1];
  300. }
  301. return rs;
  302. };
  303. var sendCronInfo = function(cron, agent, msg, info, cb) {
  304. if(isReady(info) && (cron.serverId || cron.serverType)) {
  305. if(!!cron.serverId) {
  306. agent.notifyById(cron.serverId, module.exports.moduleId, { signal: msg.signal, cron: cron });
  307. } else {
  308. agent.notifyByType(cron.serverType, module.exports.moduleId, { signal: msg.signal, cron: cron });
  309. }
  310. process.nextTick(function() {
  311. cb(null, { status: "ok" });
  312. });
  313. } else {
  314. cb(new Error('Miss necessary server parameters.'), null);
  315. }
  316. reset(info);
  317. };
  318. var startServer = function(app, msg, cb) {
  319. var server = parseArgs(msg, ServerInfo, cb);
  320. if(isReady(ServerInfo)) {
  321. runServer(app, server, cb);
  322. } else {
  323. cb(new Error('Miss necessary server parameters.'), null);
  324. }
  325. };
  326. var runServer = function(app, server, cb) {
  327. checkPort(server, function(status) {
  328. if(status === 'busy') {
  329. utils.invokeCallback(cb, new Error('Port occupied already, check your server to add.'));
  330. } else {
  331. starter.run(app, server, function(err) {
  332. if(err) {
  333. utils.invokeCallback(cb, new Error(err), null);
  334. return;
  335. }
  336. });
  337. process.nextTick(function() {
  338. utils.invokeCallback(cb, null, { status: "ok" });
  339. });
  340. }
  341. });
  342. };
  343. var startCluster = function(app, msg, cb) {
  344. var serverMap = {};
  345. var fails = [];
  346. var successFlag;
  347. var serverInfo = parseArgs(msg, ClusterInfo, cb);
  348. utils.loadCluster(app, serverInfo, serverMap);
  349. var count = utils.size(serverMap);
  350. var latch = countDownLatch.createCountDownLatch(count, function() {
  351. if(!successFlag) {
  352. utils.invokeCallback(cb, new Error('all servers start failed.'));
  353. return;
  354. }
  355. utils.invokeCallback(cb, null, fails);
  356. });
  357. var start = function(server) {
  358. return (function() {
  359. checkPort(server, function(status) {
  360. if(status === 'busy') {
  361. fails.push(server);
  362. latch.done();
  363. } else {
  364. starter.run(app, server, function(err) {
  365. if(err) {
  366. fails.push(server);
  367. latch.done();
  368. }
  369. });
  370. process.nextTick(function() {
  371. successFlag = true;
  372. latch.done();
  373. });
  374. }
  375. });
  376. })();
  377. };
  378. for(var key in serverMap) {
  379. var server = serverMap[key];
  380. start(server);
  381. }
  382. };
  383. var checkCluster = function(msg) {
  384. var flag = false;
  385. var args = msg.args;
  386. for(var i=0; i < args.length; i++) {
  387. if(utils.startsWith(args[i], Constants.RESERVED.CLUSTER_COUNT)) {
  388. flag = true;
  389. }
  390. }
  391. return flag;
  392. };
  393. var isReady = function(info) {
  394. for(var key in info) {
  395. if(info[key]) {
  396. return false;
  397. }
  398. }
  399. return true;
  400. };
  401. var reset = function(info) {
  402. for(var key in info) {
  403. info[key] = 0;
  404. }
  405. };
  406. var ServerInfo = {
  407. host: 0,
  408. port: 0,
  409. id: 0,
  410. serverType: 0
  411. };
  412. var CronInfo = {
  413. id: 0,
  414. action: 0,
  415. time: 0
  416. };
  417. var RemoveCron = {
  418. id: 0
  419. };
  420. var ClusterInfo = {
  421. host: 0,
  422. port: 0,
  423. clusterCount: 0
  424. };