不用 pm2 怎么守护进程

自上一篇 pm2 部署介绍 后,有面试官问道不用 pm2 做进程守护,该怎么办?

由于 NodeJs 是单线程执行的,所以主线程抛出了一个错误就会退出程序。线上部署当然不可能出了异常就退出了,所以需要守护进程。

node-forever

使用 forever start simple-server.js

精髓在forever-monitor

其原理就是崩溃就重启一个。

Monitor.prototype.start = function (restart) {
  var self = this,
      child;
  
  // 非重启则返回自身进程
  if (this.running && !restart) {
    process.nextTick(function () {
      self.emit('error', new Error('Cannot start process that is already running.'));
    });
    return this;
  }
  
  // 重启标志传入时,重新 fork 进程,方法里使用了 child_process.spawn 执行命令来衍生一个新进程
  child = this.trySpawn();
  if (!child) {
    process.nextTick(function () {
      self.emit('error', new Error('Target script does not exist: ' + self.args[0]));
    });
    return this;
  }

  this.ctime = Date.now();
  this.child = child;
  this.running = true;
  this.isMaster = cluster.isMaster;

  process.nextTick(function () {
    self.emit(restart ? 'restart' : 'start', self, self.data);
  });

  function onMessage(msg) {
    self.emit('message', msg);
  }

  // Re-emit messages from the child process
  this.child.on('message', onMessage);

  // 监听退出事件,崩溃时退出也算。
  child.on('exit', function (code, signal) {
    var spinning = Date.now() - self.ctime < self.minUptime;
    child.removeListener('message', onMessage);
    self.emit('exit:code', code, signal);

    function letChildDie() {
      self.running = false;
      self.forceStop = false;
      self.emit('exit', self, spinning);
    }

    function restartChild() {
      self.forceRestart = false;
      process.nextTick(function () {
        self.start(true);
      });
    }

    self.times++;
    
    // 强制关闭,当重启次数过多
    if (self.forceStop || (self.times >= self.max && !self.forceRestart)
      || (spinning && typeof self.spinSleepTime !== 'number') && !self.forceRestart) {
      letChildDie();
    }
    // 按照最小的重启时间间隔,防止不停崩溃重启
    else if (spinning) {
      setTimeout(restartChild, self.spinSleepTime);
    }
    else {
      restartChild();
    }
  });
  
  // 返回重启后的新进程
  return this;
};

shell 脚本启动守护 node

实例

WEB_DIR='/var/www/ourjs'
WEB_APP='svr/ourjs.js'

#location of node you want to use
NODE_EXE=/root/local/bin/node

while true; do
    {
        $NODE_EXE $WEB_DIR/$WEB_APP config.magazine.js
        echo "Stopped unexpected, restarting \r\n\r\n"
    } 2>> $WEB_DIR/error.log
    sleep 1
done

这个文件非常简单,只有启动的选项,守护的核心功能是由一个无限循环 while true; 来实现的,为了防止过于密集的错误阻塞进程,每次错误后间隔1秒重启服务。错误日志记录也非常简单,直接将此进程控制台当中的错误输出到error.log文件即可: 2>> $WEB_DIR/error.log 这一行, 2 代表 Error。

cluster API

Node 原生提供了 cluster,可以创建共享服务器端口的子进程。就如 EggJS 官网多进程模型的说明

+---------+                 +---------+
|  Worker |                 |  Master |
+---------+                 +----+----+
     | uncaughtException         |
     +------------+              |
     |            |              |                   +---------+
     | <----------+              |                   |  Worker |
     |                           |                   +----+----+
     |        disconnect         |   fork a new worker    |
     +-------------------------> + ---------------------> |
     |         wait...           |                        |
     |          exit             |                        |
     +-------------------------> |                        |
     |                           |                        |
    die                          |                        |
                                 |                        |
                                 |                        |

可以再业务代码之上起一个 master 进程,他 fork 出多个 worker 进程来处理任务,每当一个 worker 挂了,会有事件传回给 master,master 就能重新 fork 一份新的 worker。

那么只要 master 不挂,就能达到守护进程的目的。

posted @ 2020-05-07 00:36  Ever-Lose  阅读(800)  评论(0编辑  收藏  举报