如何在VPS上使用Node.js编写Linux守护程序

来自菜鸟教程
跳转至:导航、​搜索

状态: 已弃用

本文介绍了不再受支持的 Ubuntu 版本。 如果您当前正在运行运行 Ubuntu 12.04 的服务器,我们强烈建议您升级或迁移到受支持的 Ubuntu 版本:

原因: Ubuntu 12.04 已于 2017 年 4 月 28 日终止生命周期 (EOL) and no longer receives security patches or updates. This guide is no longer maintained.

请参阅: 本指南可能仍可用作参考,但可能不适用于其他 Ubuntu 版本。 如果可用,我们强烈建议使用为您正在使用的 Ubuntu 版本编写的指南。 您可以使用页面顶部的搜索功能来查找更新的版本。


介绍


daemon 是在后台运行且没有控制终端的程序。 它们通常用于提供后台服务。 例如,Web 服务器或数据库服务器可以作为守护程序运行。

本教程将向您展示如何使用 Node.js 编写守护程序并使用 Upstart 将其部署在您的 VPS 上。

我将专注于实现一个 standard 守护进程。 我使用 upstart 只是为了简单起见,但是你可以编写一个 System-V init 脚本或使用任何你喜欢的东西来启动你的守护进程。

要求

对于本教程,您将需要 Linux VPS(最好是 UbuntuCentOS)、Node.jsUpstart

节点.js

有几种方法可以安装 Node.js。 我认为最简单的方法是使用 nvm。 它还允许您管理不同版本的 Node

或者,您可以遵循以下指南之一:

  1. 如何在 Ubuntu 12.04 上安装 Node.js 的上游版本
  2. 通过包管理器安装 Node.js

暴发户

Upstart 预装在许多 Linux 发行版上。 如果它没有安装在您选择的发行版上,您应该能够从官方存储库安装它。 您也可以从源代码编译它。 请参阅 Upstart 入门页面 了解更多信息。

守护进程如何工作


基本上一个守护进程就像一个正常的进程一样启动。 之后会发生以下情况:

  1. 它创建一个自己的副本作为它的子进程。
  2. 子进程将自己与父进程分离。
  3. 子进程关闭其标准 文件描述符 (见下文。)
  4. 父进程退出。
  5. 守护进程在后台继续其工作。

父进程可以打开 null device 并将其附加到子进程的标准文件描述符,而不是关闭标准文件描述符。

示例守护程序


在本教程中,我们将创建一个简单的 HTTP 守护进程。

我们的守护进程将能够:

  1. 在后台启动(我们将为此使用一个名为 “daemon” 的模块。)
  2. HTTP 服务器生成多个工作人员。
  3. SIGHUP 上重新启动工作程序。
  4. SIGTERM 上终止工作人员。
  5. HTTP 服务器启动后删除工作人员的权限。

初始项目结构

以下是最初的项目结构:

node-simple-http-daemon/
|
|-- README.md
|-- bin/
|   `-- node-simple-http-daemon
`-- lib/
    `-- app.js

包.json

让我们从创建一个 package.json 文件开始:

$ npm init

安装 daemon 模块:

$ npm --save install daemon

下面是 package.json 的样子:

{
  "name": "node-simple-http-daemon",
  "version": "0.0.0",
  "description": "Simple HTTP Daemon written in Node.js",
  "main": "lib/app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node lib/app.js"
  },
  "author": "Fardjad Davari",
  "license": "MIT",
  "dependencies": {
    "daemon": "~1.1.0"
  }
}

请注意,我们添加了一个启动 脚本,因此我们可以稍后使用 npm start 命令启动应用程序。

HTTP 服务器

现在,我们将创建一个简单的 HTTP 服务器,它开始在端口 80 上侦听,并为每个请求响应 “Hello world”

将以下内容放入 lib/app.js 文件中:

/**
 * lib/app.js
 */

const PORT = 80;
const ADDRESS = '0.0.0.0';

var http = require('http');

var server = http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
});

server.listen(PORT, ADDRESS, function () {
    console.log('Server running at http://%s:%d/', ADDRESS, PORT);
    console.log('Press CTRL+C to exit');
});

您可以通过运行 sudo npm start 来启动 VPS。

守护程序可执行文件

以下是守护进程的可执行代码。

以下内容应放在 bin/node-simple-http-daemon 中:

#!/usr/bin/env node

/**
 * bin/node-simple-http-daemon
 */

// Everything above this line will be executed twice
require('daemon')();

var cluster = require('cluster');

// Number of CPUs
var numCPUs = require('os').cpus().length;

/**
 * Creates a new worker when running as cluster master.
 * Runs the HTTP server otherwise.
 */
function createWorker() {
  if (cluster.isMaster) {
    // Fork a worker if running as cluster master
    var child = cluster.fork();

    // Respawn the child process after exit
    // (ex. in case of an uncaught exception)
    child.on('exit', function (code, signal) {
      createWorker();
    });
  } else {
    // Run the HTTP server if running as worker
    require('../lib/app');
  }
}

/**
 * Creates the specified number of workers.
 * @param  {Number} n Number of workers to create.
 */
function createWorkers(n) {
  while (n-- > 0) {
    createWorker();
  }
}

/**
 * Kills all workers with the given signal.
 * Also removes all event listeners from workers before sending the signal
 * to prevent respawning.
 * @param  {Number} signal
 */
function killAllWorkers(signal) {
  var uniqueID,
      worker;

  for (uniqueID in cluster.workers) {
    if (cluster.workers.hasOwnProperty(uniqueID)) {
      worker = cluster.workers[uniqueID];
      worker.removeAllListeners();
      worker.process.kill(signal);
    }
  }
}

/**
 * Restarts the workers.
 */
process.on('SIGHUP', function () {
  killAllWorkers('SIGTERM');
  createWorkers(numCPUs * 2);
});

/**
 * Gracefully Shuts down the workers.
 */
process.on('SIGTERM', function () {
  killAllWorkers('SIGTERM');
});

// Create two children for each CPU
createWorkers(numCPUs * 2);

是时候启动我们的守护进程了! 但在此之前,我们应该修改我们的 app.js 来处理 SIGTERM

将以下内容添加到 app.js 文件的末尾:

process.on('SIGTERM', function () {
  if (server === undefined) return;
  server.close(function () {
    // Disconnect from cluster master
    process.disconnect && process.disconnect();
  });
});

使守护程序脚本可执行:

$ chmod +x bin/node-simple-http-daemon

并运行它(确保端口 80 上没有其他东西在运行):

$ sudo bin/node-simple-http-daemon

现在,您的守护进程和它的工作人员应该在后台运行。 您可以通过 cURL 发送 HTTP GET 请求来确认:

$ curl 127.0.0.1

在进行下一步之前,让我们看一下进程 ID:

$ ps -axf | grep [n]ode-simple-http-daemon | \
  awk '{ print "pid:"$2", parent-pid:"$3 }'

样本输出:

pid:33811, parent-pid:1
pid:33853, parent-pid:33811
pid:33856, parent-pid:33811
pid:33857, parent-pid:33811
pid:33858, parent-pid:33811
pid:33859, parent-pid:33811
pid:33860, parent-pid:33811
pid:33861, parent-pid:33811
pid:33862, parent-pid:33811

请注意,守护进程是 parent-pid1 的进程。

尝试通过向守护进程发送 SIGHUP 来重新启动工作程序:

$ kill -HUP 33811 # (replace 33811 with the daemon PID)

现在,如果您再次列出 PID,您应该能够看到具有新 PID 的新工作进程。 很神奇,不是吗?

要停止守护程序,只需运行:

$ kill -TERM 33811 # (replace 33811 with the daemon PID)

放弃特权

我们快完成了。 我们只需要在 VPS 启动后让工人放弃他们的特权。

修改 app.js 中的 server.listen() 回调,使其显示为:

server.listen(PORT, ADDRESS, function () {
    console.log('Server running at http://%s:%d/', ADDRESS, PORT);
    console.log('Press CTRL+C to exit');
    
    // Check if we are running as root
    if (process.getgid() === 0) {
      process.setgid('nobody');
      process.setuid('nobody');
    }
});

这就是守护进程部分。

现在您可以在系统范围内安装它:

$ sudo npm link

暴发户


制作 Upstart 工作非常容易。 在 /etc/init/node-simple-http-daemon.conf 中创建一个文件,内容如下:

# /etc/init/node-simple-http-daemon.conf

start on started network
stop on stopping network

respawn
expect daemon

exec https-proxy-daemon

现在你可以:

$ sudo start node-simple-http-daemon # Start the job
$ initctl --system node-simple-http-daemon # Check the job status
$ sudo reload node-simple-http-daemon # Send SIGHUP (restart the workers)
$ sudo stop node-simple-http-daemon # Stop the job

下一步


您可能想要修改守护程序以在它们过于频繁地被杀死(旋转)时放弃产卵工人。

在您的应用程序中拥有足够的日志记录是多么重要,我怎么强调都不过分。 请务必查看 node-bunyan

提交人:http: [[“%3Ca|//www.fardjad.com/]] [[“%3C/a|”>法贾德·达瓦里]]