UNPKG

eazy-pomelo

Version:
486 lines (451 loc) 12.8 kB
/*! * Pomelo -- consoleModule serverStop stop/kill * Copyright(c) 2012 fantasyni <fantasyni@163.com> * MIT Licensed */ var logger = require('pomelo-logger').getLogger('pomelo', __filename); var countDownLatch = require('../util/countDownLatch'); var utils = require('../util/utils'); var appUtil = require('../util/appUtil'); var Constants = require('../util/constants'); var starter = require('../master/starter'); var exec = require('child_process').exec; module.exports = function(opts) { return new Module(opts); }; module.exports.moduleId = '__console__'; var Module = function(opts) { opts = opts || {}; this.app = opts.app; this.starter = opts.starter; }; Module.prototype.monitorHandler = function(agent, msg, cb) { var serverId = agent.id; switch(msg.signal) { case 'stop': if(agent.type === Constants.RESERVED.MASTER) { return; } this.app.stop(true); break; case 'list': var serverType = agent.type; var pid = process.pid; var heapUsed = (process.memoryUsage().heapUsed/(1024 * 1024)).toFixed(2); var rss = (process.memoryUsage().rss/(1024 * 1024)).toFixed(2); var heapTotal = (process.memoryUsage().heapTotal/(1024 * 1024)).toFixed(2); var uptime = (process.uptime()/60).toFixed(2); utils.invokeCallback(cb, { serverId: serverId, body: {serverId:serverId, serverType: serverType, pid:pid, rss: rss, heapTotal: heapTotal, heapUsed:heapUsed, uptime:uptime} }); break; case 'kill': utils.invokeCallback(cb, serverId); if (agent.type !== 'master') { setTimeout(function() { process.exit(-1); }, Constants.TIME.TIME_WAIT_MONITOR_KILL); } break; case 'addCron': this.app.addCrons([msg.cron]); break; case 'removeCron': this.app.removeCrons([msg.cron]); break; case 'blacklist': if(this.app.isFrontend()) { var connector = this.app.components.__connector__; connector.blacklist = connector.blacklist.concat(msg.blacklist); } break; case 'restart': if(agent.type === Constants.RESERVED.MASTER) { return; } var self = this; var server = this.app.get(Constants.RESERVED.CURRENT_SERVER); utils.invokeCallback(cb, server); process.nextTick(function() { self.app.stop(true); }); break; default: logger.error('receive error signal: %j', msg); break; } }; Module.prototype.clientHandler = function(agent, msg, cb) { var app = this.app; //重新加载配置 appUtil.loadServers(app); switch(msg.signal) { case 'kill': kill(app, agent, msg, cb); break; case 'stop': stop(app, agent, msg, cb); break; case 'list': list(agent, msg, cb); break; case 'add': add(app, msg, cb); break; case 'addCron': addCron(app, agent, msg, cb); break; case 'removeCron': removeCron(app, agent, msg, cb); break; case 'blacklist': blacklist(agent, msg, cb); break; case 'restart': restart(app, agent, msg, cb); break; default: utils.invokeCallback(cb, new Error('The command cannot be recognized, please check.'), null); break; } }; var kill = function(app, agent, msg, cb) { var sid, record; var serverIds = []; var stopServerIds = []; var count = utils.size(agent.idMap); var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_MASTER_KILL}, function(isTimeout) { if (!isTimeout) { utils.invokeCallback(cb, null, {code: 'ok'}); } else { utils.invokeCallback(cb, null, {code: 'remained', serverIds: serverIds}); } setTimeout(function() { process.exit(-1); }, Constants.TIME.TIME_WAIT_MONITOR_KILL); }); var agentRequestCallback = function(serverId) { for (var i = 0; i < serverIds.length; ++i) { if (serverIds[i] === serverId) { serverIds.splice(i,1); latch.done(); break; } } }; for(sid in agent.idMap) { record = agent.idMap[sid]; serverIds.push(record.id); stopServerIds.push(record.id); app.set(Constants.RESERVED.STOP_SERVERS, stopServerIds); agent.request(record.id, module.exports.moduleId, { signal: msg.signal }, agentRequestCallback); } }; var stop = function(app, agent, msg, cb) { var serverIds = msg.ids; if(!!serverIds.length) { var servers = app.getServers(); app.set(Constants.RESERVED.STOP_SERVERS, serverIds); for(var i=0; i<serverIds.length; i++) { var serverId = serverIds[i]; if(!servers[serverId]) { utils.invokeCallback(cb, new Error('Cannot find the server to stop.'), null); } else { agent.notifyById(serverId, module.exports.moduleId, { signal: msg.signal }); } } utils.invokeCallback(cb, null, { status: "part" }); } else { var servers = app.getServers(); var serverIds = []; for(var i in servers){ serverIds.push(i) } app.set(Constants.RESERVED.STOP_SERVERS, serverIds); agent.notifyAll(module.exports.moduleId, { signal: msg.signal }); setTimeout(function() { utils.invokeCallback(cb, null, { status: "all" }); app.stop(true); }, Constants.TIME.TIME_WAIT_STOP); } }; var restart = function(app, agent, msg, cb) { var successFlag; var successIds = []; var serverIds = msg.ids; var type = msg.type; var servers; if(!serverIds.length && !!type) { servers = app.getServersByType(type); if(!servers) { utils.invokeCallback(cb, new Error('restart servers with unknown server type: ' + type)); return; } for(var i=0; i<servers.length; i++) { serverIds.push(servers[i].id); } } else if(!serverIds.length) { servers = app.getServers(); for(var key in servers) { serverIds.push(key); } } app.set(Constants.RESERVED.STOP_SERVERS, serverIds); var count = serverIds.length; var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function() { app.set(Constants.RESERVED.STOP_SERVERS, []); if(!successFlag) { utils.invokeCallback(cb, new Error('all servers start failed.')); return; } utils.invokeCallback(cb, null, utils.arrayDiff(serverIds, successIds)); }); var request = function(id) { return (function() { var serverInfo = null; var doStart = function(){ setTimeout(function() { runServer(app, serverInfo, function(err, status) { if(!!err) { logger.error('restart ' + id + ' failed.'); } else { successIds.push(id); successFlag = true; } latch.done(); }); }, Constants.TIME.TIME_WAIT_RESTART); } serverInfo = app.getServerById(id); if(serverInfo){ agent.request(id, module.exports.moduleId, { signal: msg.signal }, function(serverInfo) { doStart(); }); } else{ serverInfo = app.getServersFromConfig()[id]; if(serverInfo){ doStart(); } else{ latch.done(); } } })(); }; for(var j=0; j<serverIds.length; j++) { request(serverIds[j]); } }; var list = function(agent, msg, cb) { var sid, record; var serverInfo = {}; var count = utils.size(agent.idMap); var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function() { utils.invokeCallback(cb, null, { msg: serverInfo }); }); var callback = function(msg) { serverInfo[msg.serverId] = msg.body; latch.done(); }; for(sid in agent.idMap) { record = agent.idMap[sid]; agent.request(record.id, module.exports.moduleId, { signal: msg.signal }, callback); } }; var add = function(app, msg, cb) { if(checkCluster(msg)) { startCluster(app, msg, cb); } else { startServer(app, msg, cb); } reset(ServerInfo); }; var addCron = function(app, agent, msg, cb) { var cron = parseArgs(msg, CronInfo, cb); sendCronInfo(cron, agent, msg, CronInfo, cb); }; var removeCron = function(app, agent, msg, cb) { var cron = parseArgs(msg, RemoveCron, cb); sendCronInfo(cron, agent, msg, RemoveCron, cb); }; var blacklist = function(agent, msg, cb) { var ips = msg.args; for(var i=0; i<ips.length; i++) { if(!(new RegExp(/(\d+)\.(\d+)\.(\d+)\.(\d+)/g).test(ips[i]))) { utils.invokeCallback(cb, new Error('blacklist ip: ' + ips[i] + ' is error format.'), null); return; } } agent.notifyAll(module.exports.moduleId, { signal: msg.signal, blacklist: msg.args }); process.nextTick(function() { cb(null, { status: "ok" }); }); }; var checkPort = function(server, cb) { if (!server.port && !server.clientPort) { utils.invokeCallback(cb, 'leisure'); return; } var p = server.port || server.clientPort; var host = server.host; var cmd = 'netstat -tln | grep LISTEN | grep ":'; if (!utils.isLocal(host)) { cmd = 'ssh ' + host + ' ' + cmd; } exec(cmd + p + ' "', function(err, stdout, stderr) { if (stdout || stderr) { utils.invokeCallback(cb, 'busy'); } else { p = server.clientPort; exec(cmd + p + ' "', function(err, stdout, stderr) { if (stdout || stderr) { utils.invokeCallback(cb, 'busy'); } else { utils.invokeCallback(cb, 'leisure'); } }); } }); }; var parseArgs = function(msg, info, cb) { var rs = {}; var args = msg.args; for(var i =0; i<args.length; i++) { if(args[i].indexOf('=') < 0) { cb(new Error('Error server parameters format.'), null); return; } var pairs = args[i].split('='); var key = pairs[0]; if(!!info[key]) { info[key] = 1; } rs[pairs[0]] = pairs[1]; } return rs; }; var sendCronInfo = function(cron, agent, msg, info, cb) { if(isReady(info) && (cron.serverId || cron.serverType)) { if(!!cron.serverId) { agent.notifyById(cron.serverId, module.exports.moduleId, { signal: msg.signal, cron: cron }); } else { agent.notifyByType(cron.serverType, module.exports.moduleId, { signal: msg.signal, cron: cron }); } process.nextTick(function() { cb(null, { status: "ok" }); }); } else { cb(new Error('Miss necessary server parameters.'), null); } reset(info); }; var startServer = function(app, msg, cb) { var server = parseArgs(msg, ServerInfo, cb); if(isReady(ServerInfo)) { runServer(app, server, cb); } else { cb(new Error('Miss necessary server parameters.'), null); } }; var runServer = function(app, server, cb) { // checkPort(server, function(status) { // if(status === 'busy') { // utils.invokeCallback(cb, new Error('Port occupied already, check your server to add.')); // } else { starter.run(app, server, function(err) { if(err) { utils.invokeCallback(cb, new Error(err), null); return; } }); process.nextTick(function() { utils.invokeCallback(cb, null, { status: "ok" }); }); // } // }); }; var startCluster = function(app, msg, cb) { var serverMap = {}; var fails = []; var successFlag; var serverInfo = parseArgs(msg, ClusterInfo, cb); utils.loadCluster(app, serverInfo, serverMap); var count = utils.size(serverMap); var latch = countDownLatch.createCountDownLatch(count, function() { if(!successFlag) { utils.invokeCallback(cb, new Error('all servers start failed.')); return; } utils.invokeCallback(cb, null, fails); }); var start = function(server) { return (function() { // checkPort(server, function(status) { // if(status === 'busy') { // fails.push(server); // latch.done(); // } else { starter.run(app, server, function(err) { if(err) { fails.push(server); latch.done(); } }); process.nextTick(function() { successFlag = true; latch.done(); }); // } // }); })(); }; for(var key in serverMap) { var server = serverMap[key]; start(server); } }; var checkCluster = function(msg) { var flag = false; var args = msg.args; for(var i=0; i < args.length; i++) { if(utils.startsWith(args[i], Constants.RESERVED.CLUSTER_COUNT)) { flag = true; } } return flag; }; var isReady = function(info) { for(var key in info) { if(info[key]) { return false; } } return true; }; var reset = function(info) { for(var key in info) { info[key] = 0; } }; var ServerInfo = { host: 0, port: 0, id: 0, serverType: 0 }; var CronInfo = { id: 0, action: 0, time: 0 }; var RemoveCron = { id: 0 }; var ClusterInfo = { host: 0, port: 0, clusterCount: 0 };