关于PM2 restart nightmare错误

September 13, 2016

Reading time ~1 minute

线上服务nightmare使用pm2重启经常出现进程error,而使用stop && start的操作却可以正常重启, 希望从PM2的源码追踪问题原因。

1.在 bin/pm2.js 中 restart 操作支持传入与进程相关的信息然后又CLI中restart进程。


commander.command('restart <id|name|all|json|stdin...>')
  .option('--watch', 'Toggle watching folder for changes')
  .description('restart a process')
  .action(function(param) {
    // Commander.js patch 从param析取参数
    param = patchCommanderArg(param);

    async.forEachLimit(param, 1, function(script, next) {
      //调用CLI的restart方法,传入commander做回调
      CLI.restart(script, commander, next);

    }, function(err) {
      CLI.speedList(err ? 1 : 0);
    });
  });

2.在 lib/CLI.js 中的restart操作


CLI.restart = function(cmd, opts, cb) {
  //转存回调
  if (typeof(opts) == "function") {
    cb = opts;
    opts = {};
  }

  if (typeof(cmd) === 'number')
    cmd = cmd.toString();
  ....
  //调用restart
  CLI._restart(cmd, opts, cb);

};

CLI._restart = function(cmd, envs, cb) {
  CLI._operate('restartProcessId', cmd, envs, cb);
};

CLI._operate = function(action_name, process_name, envs, cb) {
  ........
  processIds([process_name], cb);

  function processIds(ids, cb) {
    async.eachLimit(ids, cst.CONCURRENT_ACTIONS, function(id, next) {
      var opts = id;
      if (action_name == 'restartProcessId') {
      	//运行环境拼装
        opts = {
          id  : id,
          env : util._extend(process.env, envs)
        };
      }
      //脚本都在这里面跑了
      Satan.executeRemote(action_name, opts, function(err, res) {
		.....
		//重启操作这么跑
        if (action_name == 'restartProcessId') {
          Satan.notifyGod('restart', id);
        } 
        .......
        .......
      });
    }, function(err) {....});
  }
}

2.在 lib/Satan.js 中的 executeRemote、notifyGod 操作


Satan.executeRemote = function executeRemote(method, env, fn) {
  ..........
  //最终到Satan中执行进程脚本
  return Satan.client.call(method, env, fn);
};

Satan.notifyGod = function(action_name, id, cb) {
  Satan.executeRemote('notifyByProcessId', {
    id : id,
    action_name : action_name, //'restartProcessId'
    manually : true
  }, function() {
    debug('God notified');
    return cb ? cb() : false;
  });
};

3.pm2-axon-rpc/lib/client.js 中的实现


var axon     = require('pm2-axon');
var rpc      = require('pm2-axon-rpc');
var req      = axon.socket('req');
Satan.client = new rpc.Client(req);

Client.prototype.call = function(name){
  var args = [].slice.call(arguments, 1, -1);
  var fn = arguments[arguments.length - 1];

  //this.sock 为req —— axon.socket('req')
  this.sock.send({
    type: 'call',
    method: name, //'restartProcessId'
    args: args
  }, function(msg){
    if ('error' in msg) {
      var err = new Error(msg.error);
      err.stack = msg.stack || err.stack;
      fn(err);
    } else {
      msg.args.unshift(null);
      fn.apply(null, msg.args);
    }
  });
};