如何在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(最好是 Ubuntu 或 CentOS)、Node.js 和 Upstart 。
节点.js
有几种方法可以安装 Node.js。 我认为最简单的方法是使用 nvm。 它还允许您管理不同版本的 Node。
或者,您可以遵循以下指南之一:
暴发户
Upstart 预装在许多 Linux 发行版上。 如果它没有安装在您选择的发行版上,您应该能够从官方存储库安装它。 您也可以从源代码编译它。 请参阅 Upstart 入门页面 了解更多信息。
守护进程如何工作
基本上一个守护进程就像一个正常的进程一样启动。 之后会发生以下情况:
- 它创建一个自己的副本作为它的子进程。
- 子进程将自己与父进程分离。
- 子进程关闭其标准 文件描述符 (见下文。)
- 父进程退出。
- 守护进程在后台继续其工作。
父进程可以打开 null device 并将其附加到子进程的标准文件描述符,而不是关闭标准文件描述符。
示例守护程序
在本教程中,我们将创建一个简单的 HTTP 守护进程。
我们的守护进程将能够:
- 在后台启动(我们将为此使用一个名为 “daemon” 的模块。)
- 为 HTTP 服务器生成多个工作人员。
- 在 SIGHUP 上重新启动工作程序。
- 在 SIGTERM 上终止工作人员。
- 在 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-pid 为 1 的进程。
尝试通过向守护进程发送 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。