node.js做一个小程序
统计数据显示 , Node.js作为服务器端运行时环境正变得越来越流行,尤其是对于高流量的网站。 此外,几个框架的可用性使其成为快速原型制作的良好环境。 Node.js具有事件驱动的架构,利用了非阻塞的I / O API,该API允许异步处理请求。
Node.js的重要功能(通常很少强调)之一是其可伸缩性。 实际上,这是一些流量大的大型公司 (例如,Microsoft,Yahoo,Uber和Walmart) 将Node.js集成到其平台中 ,甚至将其服务器端操作完全转移到Node.js(例如,)的主要原因。 ,PayPal,eBay和Groupon)。
每个Node.js进程都在一个线程中运行,默认情况下,它在32位系统上的内存限制为512MB,在64位系统上的内存限制为1GB。 尽管在32位系统上内存限制可以提高到〜1GB,在64位系统上内存限制可以达到〜1.7GB ,但是内存和处理能力仍然可能成为各种进程的瓶颈。
Node.js为扩展应用程序提供的优雅解决方案是使用Node.js术语将单个进程拆分为多个进程或工作程序。 这可以通过集群模块来实现。 群集模块允许您创建子进程(工作程序),这些子进程与主Node进程(主程序)共享所有服务器端口。
在本文中,您将看到如何创建一个Node.js集群来加速您的应用程序。
Node.js集群模块:它是什么以及如何工作
群集是在父节点进程下运行的相似工作程序的池。 使用child_processes
模块的fork()
方法生成工作child_processes
。 这意味着工作人员可以共享服务器句柄,并使用IPC(进程间通信)与父Node进程进行通信。
他的主要过程负责启动和控制工人。 您可以在主流程中创建任意数量的工作程序。 此外,请记住,默认情况下,传入连接以轮询方式在工作进程之间分配(Windows除外)。 实际上,还有另一种分配传入连接的方法,在这里我将不讨论,该方法将分配移交给OS(Windows中的默认设置)。 Node.js文档建议使用默认的轮询样式作为调度策略。
尽管在理论上使用集群模块听起来很复杂,但是实现起来却非常简单。 要开始使用它,必须将其包含在Node.js应用程序中:
var cluster = require('cluster);
集群模块多次执行相同的Node.js进程。 因此,您需要做的第一件事是确定代码的哪一部分用于主流程,哪一部分用于工作人员。 群集模块使您可以如下识别主进程:
if(cluster.isMaster) { ... }
主进程是您启动的进程,它依次初始化工作进程。 要在主进程中启动工作进程,我们将使用fork()
方法:
cluster.fork();
此方法返回一个工作程序对象,其中包含有关派生工作程序的一些方法和属性。 我们将在以下部分中看到一些示例。
集群模块包含几个事件。 与工人上班和下班的时刻有关的两个常见事件是online
事件和exit
事件。 online
当工人分叉并发送在线消息发出。 当工作进程死亡时, exit
被发出。 稍后,我们将看到如何使用这两个事件来控制工作人员的生命周期。
现在,让我们将到目前为止所看到的一切放在一起,并显示一个完整的工作示例。
例子
本节有两个示例。 第一个是一个简单的应用程序,显示了如何在Node.js应用程序中使用集群模块。 第二个是利用Node.js集群模块的Express服务器,它是我通常在大型项目中使用的生产代码的一部分。 这两个示例都可以从GitHub下载 。
如何在Node.js应用程序中使用集群模块
在第一个示例中 ,我们设置了一个简单的服务器,该服务器使用包含处理该请求的工作进程ID的消息来响应所有传入的请求。 主过程分叉了四个工人。 在每个端口中,我们开始侦听端口8000的传入请求。
下面显示了实现我刚刚描述的代码:
var cluster = require('cluster');
var http = require('http');
var numCPUs = 4;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
http.createServer(function(req, res) {
res.writeHead(200);
res.end('process ' + process.pid + ' says hello!');
}).listen(8000);
}
您可以通过启动服务器(运行命令node simple.js
)并访问URL http://127.0.0.1:8000/来在服务器上对其进行测试。 收到请求后,将它们一次分配给每个工作人员。 如果有工作人员,它将立即开始处理请求; 否则它将被添加到队列中。
在上面的示例中,有几点不是很有效。 例如,想象一个工人是否由于某种原因去世。 在这种情况下,您将失去一名工作人员,并且如果再次发生同样的情况,您将最终获得一个没有任何工作人员来处理传入请求的主流程。 另一个问题与工人人数有关。 您将应用程序部署到的系统中有不同数量的核心/线程。 在上述示例中,要使用系统的所有资源,您必须手动检查每个部署服务器的规格,找到可用的线程数,然后在代码中对其进行更新。 在下一个示例中,我们将看到如何通过Express服务器提高代码效率。
如何开发高度可扩展的Express服务器
Express是Node.js最受欢迎的Web应用程序框架之一(如果不是最受欢迎的话)。 在SitePoint上,我们已经介绍了几次。 如果您有兴趣了解更多信息,建议您阅读以下文章: 使用Express 4创建RESTful API和构建Node.js支持的Chatroom Web App:Express和Azure 。
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
第二个示例显示了我们如何开发高度可扩展的Express服务器。 它还演示了如何迁移单个流程服务器以利用几行代码的集群模块。
var cluster = require('cluster');
if(cluster.isMaster) {
var numWorkers = require('os').cpus().length;
console.log('Master cluster setting up ' + numWorkers + ' workers...');
for(var i = 0; i < numWorkers; i++) {
cluster.fork();
}
cluster.on('online', function(worker) {
console.log('Worker ' + worker.process.pid + ' is online');
});
cluster.on('exit', function(worker, code, signal) {
console.log('Worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal);
console.log('Starting a new worker');
cluster.fork();
});
} else {
var app = require('express')();
app.all('/*', function(req, res) {res.send('process ' + process.pid + ' says hello!').end();})
var server = app.listen(8000, function() {
console.log('Process ' + process.pid + ' is listening to all incoming requests');
});
}
此示例的第一个附加功能是使用Node.js os
模块获取CPU内核数。 os
模块包含一个cpus()
函数,该函数返回一个CPU内核数组。 使用这种方法,我们根据服务器规格来最大程度地确定要动态派生的工作人员数量。
第二个也是更重要的补充是处理工人的死亡。 当工作人员死亡时,集群模块将发出exit
事件。 可以通过侦听事件并在事件发出时执行回调函数来处理它。 您可以通过编写诸如cluster.on('exit', callback);
类的语句来实现cluster.on('exit', callback);
。 在回调中,我们派生了一个新的工作程序,以维持预期的工作程序数。 即使存在一些未处理的异常,这也使我们能够保持应用程序运行。
在此示例中,我还为online
事件设置了一个侦听器,该事件在工作者被派生并准备接收传入请求时发出。 这可以用于日志记录或其他操作。
性能比较
有几种工具可以用来对API进行基准测试,但是这里我使用Apache Benchmark工具来分析使用集群模块如何影响应用程序的性能。
为了设置测试, 我开发了一个Express服务器 , 该服务器具有一个路由和该路由的一个回调。 在回调中,将执行虚拟操作,然后返回一条短消息。 服务器有两种版本:一种没有工作程序,一切都在主进程中进行;另一种有8个工作程序(因为我的机器有8个内核)。 下表显示了合并集群模块如何增加每秒处理的请求数。
并发连接 | 1个 | 2 | 4 | 8 | 16 |
---|---|---|---|---|---|
单工序 | 654 | 711 | 783 | 776 | 754 |
8名工人 | 594 | 1198 | 2110 | 3010 | 3024 |
(每秒处理的请求)
进阶运作
尽管使用集群模块相对简单,但是可以使用工作程序执行其他操作。 例如,您可以使用群集模块在应用程序中实现(几乎!)零停机时间。 我们将在一段时间内看到如何执行其中一些操作。
主人与工人之间的沟通
有时,您可能需要将消息从主服务器发送给工作人员,以分配任务或执行其他操作。 作为回报,工人可能需要通知船长任务已完成。 要侦听消息,应在master和worker中都设置message
事件的事件侦听器:
worker.on('message', function(message) {
console.log(message);
});
worker
对象是fork()
方法返回的引用。 要侦听工作者中主服务器的消息:
process.on('message', function(message) {
console.log(message);
});
消息可以是字符串或JSON对象。 要将消息从主服务器发送给特定的工作人员,您可以编写如下所示的代码:
worker.send('hello from the master');
同样,要将消息从工作人员发送给主服务器,您可以编写:
process.send('hello from worker with id: ' + process.pid);
在Node.js中,消息是通用消息,没有特定类型。 因此,优良作法是将消息作为JSON对象发送,并附带一些有关消息类型,发件人和内容本身的信息。 例如:
worker.send({
type: 'task 1',
from: 'master',
data: {
// the data that you want to transfer
}
});
这里要注意的重要一点是消息事件回调是异步处理的。 没有定义的执行顺序。 您可以在GitHub上找到主人和工人之间进行交流的完整示例 。
零停机时间
使用工作程序可以实现的一个重要结果是(几乎)零停机时间服务器。 在主进程中,对应用程序进行更改后,您可以一次终止并重新启动工作进程。 这样,您就可以在运行旧版本的同时加载新版本。
为了能够在运行时重新启动应用程序,必须牢记两点。 首先,主进程一直运行,只有工作进程被终止并重新启动。 因此,重要的是保持您的主流程简短,仅负责管理工人。
其次,您需要以某种方式通知主进程它需要重新启动工作进程。 有几种执行此操作的方法,包括用户输入或查看文件中的更改。 后者效率更高,但是您需要标识要在主过程中监视的文件。
我建议重新启动您的工作程序是尝试首先安全地关闭他们; 然后,如果它们没有安全地终止,则将其杀死。 您可以通过向工作人员发送shutdown
消息来完成前者:
workers[wid].send({type: 'shutdown', from: 'master'});
并在工作程序消息事件处理程序中启动安全关机:
process.on('message', function(message) {
if(message.type === 'shutdown') {
process.exit(0);
}
});
要对所有工作程序执行此操作,可以使用集群模块的workers
属性,该属性保留对所有正在运行的工作程序的引用。 我们还可以将所有任务包装在主进程的一个函数中,只要我们要重新启动所有工作进程,就可以调用该任务。
function restartWorkers() {
var wid, workerIds = [];
for(wid in cluster.workers) {
workerIds.push(wid);
}
workerIds.forEach(function(wid) {
cluster.workers[wid].send({
text: 'shutdown',
from: 'master'
});
setTimeout(function() {
if(cluster.workers[wid]) {
cluster.workers[wid].kill('SIGKILL');
}
}, 5000);
});
};
我们可以从集群模块中的workers
对象获取所有正在运行的worker的ID。 该对象保留对所有正在运行的工作程序的引用,并且在工作程序终止和重新启动时会动态更新。 首先,我们将所有正在运行的工作程序的ID存储在workerIds
数组中。 这样,我们避免了重新启动新分支的工人。
然后,我们要求每个工人安全关机。 如果5秒钟后工作程序仍在运行并且仍存在于workers
对象中,则我们在工作程序上调用kill
函数以强制其关闭。 您可以在GitHub上找到一个实际示例 。
结论
可以使用群集模块并行化Node.js应用程序,以便更有效地使用系统。 只需使用几行代码即可同时运行多个进程,这使迁移相对容易,因为Node.js处理了困难的部分。
正如我在性能比较中所显示的,通过以更有效的方式利用系统资源,可以显着提高应用程序性能。 除了性能之外,还可以通过在应用程序运行时重新启动工作程序来提高应用程序的可靠性和正常运行时间。
话虽如此,在考虑在应用程序中使用集群模块时,您需要格外小心。 建议将群集模块主要用于Web服务器。 在其他情况下,您需要仔细研究如何在工作人员之间分配任务,以及如何有效地沟通工作人员与主管之间的进度。 即使对于Web服务器,在对应用程序进行任何更改之前,请确保单个Node.js进程是瓶颈(内存或CPU),因为您可能会在更改中引入一些错误。
最后一件事,Node.js网站上有关于集群模块的出色文档 。 因此,请务必检查一下!
翻译自: https://www.sitepoint.com/how-to-create-a-node-js-cluster-for-speeding-up-your-apps/
node.js做一个小程序