不用 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 不掛,就能達到守護進程的目的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM