如何在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。