node.js性能优化和单线程架构

时间:2016-08-26 07:49:30

标签: node.js

我正在使用express运行Node.js应用程序,并希望开始提高其性能。定义了几条路线。我们有一个基本的例子:

app.get('/users', function (req, res) {
    User.find({}).exec(function(err, users) {
        res.json(users);
    }
});

假设我们有3个客户端A,B和C,他们尝试使用此路由。他们的请求按照A,B,C的顺序到达服务器,两者之间有1毫秒的差异。

1。如果我正确理解了node.js架构,那么每个请求都会立即处理,因为Users.find()是异步的并且有非阻塞代码?

让我们用同步调用展开这个例子:

 app.get('/users', function (req, res) {
        var parameters = getUserParameters();

        User.find({parameters}).exec(function(err, users) {
            res.json(users);
        }
    });

相同的请求,相同的订单。 getUserParameters()需要50毫秒才能完成。

2。 A将进入路由回调函数并阻塞node.js线程50毫秒。 B和C将无法进入该功能并且必须等待。当A完成getUsersParameters()时,它将继续使用异步User.find()函数,B现在将进入路由回调函数。 C仍然需要等待50多毫秒。当B进入异步功能时,可以最终处理C的请求。合在一起:C必须等待50毫秒才能完成A,50毫秒需要B完成,50毫秒才能完成(为简单起见,我们忽略异步功能的等待时间)?

现在假设我们还有一条路由,只能由管理员访问,并且每分钟都会通过crontab调用。

app.get('/users', function (req, res) {  
    User.find({}).exec(function(err, users) {
        res.json(users);
    }
});

app.get('/admin-route', function (req, res) {
    blockingFunction(); // this function takes 2 seconds to complete
});

第3。当请求X命中admin-route并且blockingFunction()被调用时,A,B和C将在X的请求之后立即调用/users,必须等待2秒,直到他们甚至进入路由回调-function吗

4。我们是否应该将每个自定义函数(即使只需要4毫秒)作为具有回调的异步函数来进行?

4 个答案:

答案 0 :(得分:6)

答案是"是",#3:阻塞意味着阻止事件循环,这意味着任何I / O(如处理HTTP请求)都将被阻止。在这种情况下,应用程序在2秒内似乎没有响应。

但是,您必须为同步代码执行相当繁琐的工作需要2秒钟(非常繁重的计算,或者使用*Sync()等模块提供的大量fs方法。如果你真的不能使代码异步,你应该考虑在一个单独的过程中运行它。

关于#4:如果你可以轻松地使它异步,你可能应该。但是,只是让你的同步函数接受回调并不会奇迹般地使它异步。这取决于函数的作用,以及如何使其异步。

答案 1 :(得分:0)

基本原则是锁定CPU(例如长时间运行循环)或使用I / O或网络的任何东西必须是异步的。您还可以考虑将CPU密集型逻辑从节点JS中移出,也许转移到Java / Python模块中,该模块公开了JS可以调用的WebService。

顺便说一下,看看这个模块(可能不是生产就绪的)。它在NodeJS中引入了多线程的概念:https://www.npmjs.com/package/webworker-threads

答案 2 :(得分:0)

#3是

#4 Node.js用于异步编程,因此可以采用这种方法来避免性能上的惊喜

同时,您可以使用Node.js的cluster模块来提高应用的性能和吞吐量。

答案 3 :(得分:0)

您可能需要先垂直扩展应用程序。查看Node.js群集模块。您可以通过在每个核心上生成工作人员来利用机器的所有核心。群集是在父节点进程下运行的类似工作池。使用child_processes模块的fork()方法生成worker。这意味着工作人员可以共享服务器句柄并使用进程间通信与父节点进程进行通信。

var cluster = require('cluster')
var http = require('http')
var os = require('os')
var numCPUs = os.cpus().length

if (cluster.isMaster) {
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork()
    }
} else {
    // Define express routes and listen here
}