Heim > Web-Frontend > js-Tutorial > Lassen Sie uns über verschiedene Situationen sprechen, die dazu führen können, dass der Node.js-Prozess beendet wird

Lassen Sie uns über verschiedene Situationen sprechen, die dazu führen können, dass der Node.js-Prozess beendet wird

青灯夜游
Freigeben: 2022-04-02 20:09:02
nach vorne
3632 Leute haben es durchsucht

Dieser Artikel befasst sich mit dem Beenden des Prozesses Node und stellt verschiedene Situationen vor, die zum Beenden des Node.js-Prozesses führen können. Ich hoffe, dass er für alle hilfreich ist!

Lassen Sie uns über verschiedene Situationen sprechen, die dazu führen können, dass der Node.js-Prozess beendet wird

Nach der Veröffentlichung unseres Dienstes wird er zwangsläufig von der Betriebsumgebung (z. B. Container, PM2 usw.) geplant, Dienst-Upgrades führen zu Neustarts und verschiedene Ausnahmen führen im Allgemeinen zum Absturz des Prozesses. In der Betriebsumgebung gelten Einschränkungen für den Dienstprozess. Wenn der Prozess abnormal ist, gibt es auch eine fortlaufende Upgrade-Strategie. Die Planungsstrategie der laufenden Umgebung behandelt unseren Serviceprozess jedoch als Black Box und kümmert sich nicht um die internen Betriebsbedingungen des Serviceprozesses. Daher muss unser Serviceprozess die Planungsaktionen der laufenden Umgebung aktiv erkennen und dann ausführen einige Bereinigungsaktionen beenden.

Deshalb werden wir heute die verschiedenen Situationen klären, die zum Beenden des Node.js-Prozesses führen können, und was wir tun können, indem wir diese Prozessbeendigungsereignisse abhören.

Prinzip

Ein Prozess muss in zwei Situationen beendet werden: Zum einen, wenn der Prozess von sich aus beendet wird, und zum anderen, wenn er ein Systemsignal empfängt, das den Prozess zum Beenden auffordert.

Systemsignalbenachrichtigung zum Beenden

Allgemeine Systemsignale sind im Node.js offiziellen Dokument aufgeführt. Wir konzentrieren uns hauptsächlich auf einige:

  • SIGHUP: Stoppen Sie den Prozess nicht mit Strg+C, sondern schließen Sie ihn es direkt Das Befehlszeilenterminal löst dieses Signal aus
  • SIGINT: Wird ausgelöst, wenn Strg + C gedrückt wird, um den Prozess zu stoppen, wenn pm2 den untergeordneten Prozess neu startet oder stoppt. Dieses Signal wird auch an den untergeordneten Prozess gesendet
  • SIGTERM: Im Allgemeinen Wird verwendet, um den Prozess zum ordnungsgemäßen Beenden zu benachrichtigen. Wenn k8s beispielsweise einen Pod löscht, sendet es ein SIGTERM-Signal an den Pod. Der Pod kann innerhalb des Timeout-Zeitraums (Standard 30 Sekunden) einige Exit-Reinigungsaktionen durchführen Fenstersystem: Durch Drücken von Strg+Pause wird dieses Signal ausgelöst
  • SIGKILL: Der Prozess kann keine Reinigungsaktionen ausführen. Wenn Sie den Befehl kill -9 pid ausführen, empfängt der Prozess dieses Signal. Wenn k8s einen Pod löscht, wenn es länger als 30 Sekunden dauert und der Pod nicht beendet wurde, sendet k8s ein SIGKILL-Signal an den Pod und beendet den Pod-Prozess sofort, wenn pm2 den Prozess neu startet oder stoppt, wenn er länger als 1,6 Sekunden dauert Sekunden und der Prozess wurde nicht beendet, sendet er außerdem ein SIGKILL-Signal
  • Wenn ein nicht erzwungenes Exit-Signal empfangen wird, kann der Node.js-Prozess das Exit-Signal abhören und eine benutzerdefinierte Exit-Logik ausführen. Wir haben beispielsweise ein CLI-Tool geschrieben, dessen Ausführung lange dauert. Wenn der Benutzer den Prozess über Strg + C beenden möchte, bevor die Aufgabe abgeschlossen ist, kann er zum Warten aufgefordert werden:
const readline = require('readline');

process.on('SIGINT', () => {
  // 我们通过 readline 来简单地实现命令行里面的交互
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  rl.question('任务还没执行完,确定要退出吗?', answer => {
    if (answer === 'yes') {
      console.log('任务执行中断,退出进程');
      process.exit(0);
    } else {
      console.log('任务继续执行...');
    }
    rl.close();
  });
});



// 模拟一个需要执行 1 分钟的任务
const longTimeTask = () => {
  console.log('task start...');
  setTimeout(() => {
    console.log('task end');
  }, 1000 * 60);
};

longTimeTask();
Nach dem Login kopieren

Der Effekt ist Wie folgt: Jedes Mal, wenn der Benutzer Strg + C drückt, wird der Benutzer aufgefordert:

Lassen Sie uns über verschiedene Situationen sprechen, die dazu führen können, dass der Node.js-Prozess beendet wird

Der Prozess wurde aktiv beendet Wird während der Codeausführung ausgelöst, können Sie „process.on('uncaughtException“) übergeben, um diese Situation zu überwachen.

Während der Codeausführung wird eine nicht behandelte Versprechensablehnung ausgelöst (Node.js v16 führt dazu, dass der Prozess beendet wird). Sie können diese Situation durch den Prozess überwachen .on('unhandledRejection') Situation

EventEmitter löst ein nicht überwachtes Fehlerereignis aus

Die Funktion „process.exit“ wird im Code aktiv aufgerufen, um den Prozess zu beenden. Sie können ihn über „process.on('exit“)“ überwachen Die Ereigniswarteschlange von Node.js ist leer. Sie können einfach denken, dass es keinen Code gibt, der ausgeführt werden muss, und zwar über den Prozess.on('exit')
  • Wir wissen, dass pm2 die Wirkung eines Daemon-Prozesses hat . Wenn Ihr Prozess mit einem Fehler beendet wird, wird pm2 Ihren Prozess neu starten. Im
  • Cluster-Modus
  • von js wird der Effekt eines untergeordneten Daemon-Prozesses erreicht:
  • const cluster = require('cluster');
    const http = require('http');
    const numCPUs = require('os').cpus().length;
    const process = require('process');
    
    // 主进程代码
    if (cluster.isMaster) {
      console.log(`启动主进程: ${process.pid}`);
      // 根据 cpu 核数,创建工作进程
      for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
      // 监听工作进程退出事件
      cluster.on(&#39;exit&#39;, (worker, code, signal) => {
        console.log(`工作进程 ${worker.process.pid} 退出,错误码: ${code || signal}, 重启中...`);
        // 重启子进程
        cluster.fork();
      });
    }
    
    // 工作进程代码
    if (cluster.isWorker) {
      // 监听未捕获错误事件
      process.on(&#39;uncaughtException&#39;, error => {
        console.log(`工作进程 ${process.pid} 发生错误`, error);
        process.emit(&#39;disconnect&#39;);
        process.exit(1);
      });
      // 创建 web server
      // 各个工作进程都会监听端口 8000(Node.js 内部会做处理,不会导致端口冲突)
      http.createServer((req, res) => {
        res.writeHead(200);
        res.end(&#39;hello world\n&#39;);
      }).listen(8000);
      console.log(`启动工作进程: ${process.pid}`);
    }
    Nach dem Login kopieren
  • Anwendungspraxis

Oben haben wir verschiedene Situationen analysiert, in denen der Node.js-Prozess beendet wird. Lassen Sie uns nun ein Tool erstellen, das das Beenden des Prozesses überwacht eigene Exit-Logik:

Signalnummer:

接下来我们要实现真正的进程退出函数 handleExit,因为用户传入的任务函数可能是同步的,也可能是异步的;我们可以借助 process.nextTick 来保证用户的同步代码都已经执行完成,可以简单理解 process.nextTick 会在每个事件循环阶段的同步代码执行完成后执行(理解 process.nextTick);针对异步任务,我们需要用户调用 callback 来告诉我们异步任务已经执行完成了:

// 标记是否正在退出,避免多次执行
let isExiting = false;
const handleExit = (code, error) => {
  if (isExiting) return;
  isExiting = true;

  // 标记已经执行了退出动作,避免多次调用
  let hasDoExit = fasle;
  const doExit = () => {
      if (hasDoExit) return;
      hasDoExit = true
      process.nextTick(() => process.exit(code))
  }

  // 记录有多少个异步任务
  let asyncTaskCount = 0;
  // 异步任务结束后,用户需要调用的回调
  let ayncTaskCallback = () => {
      process.nextTick(() => {
        asyncTaskCount--
        if (asyncTaskCount === 0) doExit() 
      })
  }
  // 执行所有的退出任务

  tasks.forEach(taskFn => {
      // 如果 taskFn 函数的参数个数大于 1,认为传递了 callback 参数,是一个异步任务
      if (taskFn.length > 1) {
         asyncTaskCount++
         taskFn(error, ayncTaskCallback)
      } else {
          taskFn(error)
      }
  });

  // 如果存在异步任务
  if (asyncTaskCount > 0) {
      // 超过 10s 后,强制退出
      setTimeout(() => {
          doExit();
      }, 10 * 1000)
  } else {
      doExit()
  }
};
Nach dem Login kopieren

至此,我们的进程退出监听工具就完成了,完整的实现可以查看这个开源库 async-exit-hook

https://github.com/darukjs/daruk-exit-hook

进程优雅退出

通常我们的 web server 在重启、被运行容器调度(pm2 或者 docker 等)、出现异常导致进程退出时,我们希望执行退出动作,如完成已经连接到服务的请求响应、清理数据库连接、打印错误日志、触发告警等,做完退出动作后,再退出进程,我们可以使用刚才的进程退出监听工具实现:

const http = require(&#39;http&#39;);

// 创建 web server
const server = http.createServer((req, res) => {
  res.writeHead(200);
  res.end(&#39;hello world\n&#39;);
}).listen(8000);

// 使用我们在上面开发的工具添加进程退出任务
addExitTask((error, callback) => {
   // 打印错误日志、触发告警、释放数据库连接等
   console.log(&#39;进程异常退出&#39;, error)
   // 停止接受新的请求
   server.close((error) => {
       if (error) {
         console.log(&#39;停止接受新请求错误&#39;, error)
       } else {
         console.log(&#39;已停止接受新的请求&#39;)
       }
   })
   // 比较简单的做法是,等待一定的时间(这里我们等待 5s),让存量请求执行完毕
   // 如果要完全保证所有请求都处理完毕,需要记录每一个连接,在所有连接都释放后,才执行退出动作
   // 可以参考开源库 https://github.com/sebhildebrandt/http-graceful-shutdown
   setTimout(callback, 5 * 1000)
})
Nach dem Login kopieren

总结

通过上面的文字,相信你已经对导致 Node.js 进程退出的各种情况心里有数了。在服务上线后,虽然 k8s、pm2 等工具能够在进程异常退出时,不停地拉起进程,保证服务的可用性,但我们也应该在代码中主动感知进程的异常或者被调度的情况,从而能够更早发现问题。

更多node相关知识,请访问:nodejs 教程

Das obige ist der detaillierte Inhalt vonLassen Sie uns über verschiedene Situationen sprechen, die dazu führen können, dass der Node.js-Prozess beendet wird. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage