nodejs平滑重启

  1. pm2(restart, reload 以及 gracefulReload)

    1. restart: 直接关闭旧服务然后启动新服务,会造成已建立的连接失效
    2. reload: 平滑更新,先启动若干个新服务,同时停止旧服务接收请求。等待旧服务都停止服务后,关闭旧服务。和 cluster 的代码原理类似,有可能因为要等待连接关闭造成重启时间比较长。
    3. gracefulReload: 平滑更新,和 reload 的区别是 gracefulReload 会发送一个 shutdown 消息给旧服务,具体的停服逻辑可以由程序自己实现,比较灵活。
  2. 多机器 + nginx负载均衡模块

    1. 更新服务时只需要逐台部署,保证同一时刻至少有一台机器在提供服务,nginx 就会将流量自动分配到正常服务的机器上
  3. Node 本身的 cluster 模块

    1. 发一个重启信号给 Master,例如 kill -USR2 MASTER_PID
    2. Master 起 n 个新的服务,开始监听请求
    3. Master 停止原先旧服务的监听,并等待旧服务的所有连接结束
    4. 关闭旧服务

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      var cluster = require('cluster');
      var http = require('http');

      if (cluster.isMaster) {
      cluster.fork();

      cluster.on('exit', function(worker, code, signal) {
      console.log('worker ' + worker.process.pid + ' 退出');
      });

      cluster.on('listening', function(worker, code, signal) {
      console.log('worker ' + worker.process.pid + ' 开始服务');
      });

      cluster.on('disconnect', function(worker, code, signal) {
      console.log('worker ' + worker.process.pid + ' 停止服务');
      });

      process.on('SIGUSR2', function () {
      // 保存旧 worker 的列表,cluster.workers 是个 map
      var oldWorkers = Object.keys(cluster.workers).map(function (idx) {
      return cluster.workers[idx];
      });

      // 起新服务
      cluster.fork();

      // 当新服务起起来之后,关闭所有的旧 worker
      cluster.once('listening', function (worker) {
      oldWorkers.forEach(function (worker) {
      // disconnect 会停止接收新请求,等待旧请求结束后再结束进程
      worker.disconnect();
      });
      });
      });
      } else {
      http.createServer(function(req, res) {
      // 模拟慢速请求
      setTimeout(function () {
      res.writeHead(200);
      res.end("hello world\n");
      }, 15000);
      }).listen(8000);
      }

nodejs runtime架构

什么是runtime

  1. 程序分为几个状态,编辑时->编译时->静态时->运行时
  2. 比如有些错误在编译的时候是不会出现的,就是程序在语法上没有问题。但在运行时,因为缺少资源等因素可能出现运行时错误。叫做runtime error!

nodejs runtime架构

runtime

  1. 用户代码:由程序员编写的 Javascript 应用程序代码。
  2. Node.js API: Node 提供的内置方法,可以在用户代码中使用(例如 用于使用 HTTP 方法的 HTTP modules、crypto module、用于文件系统操作的 fs module、用于网络请求的 net 等……)。 有关 Node 提供的方法的完整列表,您可以在此处查看文档。 此外,您可以在此处找到源代码实现。 Node 的 API 是用 Javascript 编写的。
  3. Bindings 和 C++扩展插件: 在阅读 Node 时,您会看到 V8 是用 C++ 编写的,Libuv 是用 C 编写的,等等。 基本上,所有模块都是用 C 或 C++ 编写的,因为这些语言在处理底层任务和使用 OS API 方面非常出色和快速。 但是上层的Javascript代码怎么可能用其他语言去写代码呢? 这就是 bindings 的作用。 它们充当两层之间的粘合剂,因此 Node 可以顺利使用 C 或 C++ 编写的低级代码。 那么,如果我们想自己添加一个C++模块应该怎么做呢? 我们首先用 C++ 实现模块,然后为此编写 bindings代码。 我们编写的这段代码称为扩展插件。 更多信息可以在这里找到。
  4. Node’s 依赖: 这一层代表 Node 使用的底层库。 最大的依赖是谷歌的 V8 引擎和 Libuv。 其他库包括 OpenSSL(用于 SSL、TLS 和其他基本加密功能)、HTTP 解析器(用于解析 HTTP 请求和响应)、C-Ares(用于异步 DNS 请求)和 Zlib(用于快速压缩和解压缩)。
  5. 操作系统: 这是表示上述库所使用的 OS API(系统调用)的最底层。 由于 OS-es 不同,这些库包括 Windows 和 Unix 变体的实现,这使得 Node 平台独立。

来源于:https://juejin.cn/post/6979790275879125006