在node.js中使用worker/后台进程与异步调用的区别

3
我希望了解将数据库或其他异步调用转移到工作进程中是否有任何好处,特别是我正在使用Heroku和PostgreSQL。我已经详细阅读了有关Node.js的文章,并学习如何构建服务器,以使事件循环不被阻塞,并且智能架构不会让传入请求挂起超过300毫秒左右。
假设我有以下内容:
 app.get('/getsomeresults/:query', function(request, response){
    var foo = request.params.query;
    pg.connect(process.env.DATABASE_URL, function(err, client, done) {
            client.query("SELECT * FROM users WHERE cat=$1", [foo], 
            function(err, result){
            //do some stuff with result.rows that may take 1000ms
            response.json({some:data})
            });
    });
 });

由于PostgreSQL本质上是异步的,因此创建一个工作进程来处理从初始数据库调用返回的结果集是否有任何真正的好处?

2个回答

0

在另一个进程中运行异步函数并不能带来任何好处,因为真正的工作(运行SQL查询)已经在另一个进程(postgres)中运行了。基本上,异步/事件驱动的设计模式是一个轻量级的进程管理器,用于运行在您进程之外的事物。

但是,我注意到在您的评论中,如果回调函数处理确实占用了很多CPU时间(如果那是真的),那么代码的那一部分确实会从在另一个进程中运行中获益——它可以释放主进程以接受传入的请求。

有两种方法来构建这样的代码。要么在单独的进程中运行异步函数(以便回调不会被阻塞),要么只需在单独的进程中将回调的相关部分作为一个函数来运行。


嗯,所以回调函数 //做一些事情 会阻塞主进程吗? - OliverJ90
只要“do stuff”部分不是异步的,那么它就会阻塞。事件循环只是一个对“while(){switch(){}}”循环(换句话说,状态机)的高级抽象。 - slebetman
啊,看来我错误地认为从最初的client.query到回调函数都是异步的。 - OliverJ90

0

从一个单独的进程调用client.query并不能给你带来真正的好处,因为在node-pg中向服务器发送查询已经是异步操作了。然而,真正的问题在于回调函数的长时间执行。回调在主事件循环中同步运行并阻塞其他操作,因此最好将其设置为非阻塞。

选项1:分叉子进程

每次执行回调时创建一个新进程并不是一个好主意,因为每个Node.js进程都需要自己的环境,这需要耗费时间来设置。相反,最好在服务器启动时创建多个服务器进程,并让它们同时处理请求。

选项2:使用Node.js集群

幸运的是,Node.js提供了cluster接口来实现这一点。集群使您能够从一个主进程处理多个工作进程。它甚至支持连接池,因此您可以在每个子进程中简单地创建HTTP服务器,传入的请求将自动分配给它们(node-pg也支持连接池)。

集群解决方案也很好,因为您不必在代码中进行大量更改。只需编写主进程代码并将现有代码作为工作进程启动即可。

Node.js 集群的官方文档非常详细地解释了集群的各个方面,因此我在这里不会详细介绍。这里只提供一个可能的主代码的简短示例:

var cluster = require("cluster");
var os = require("os");
var http = require("http");

if (cluster.isMaster)
    master();
else
    worker();

function master() {
    console.info("MASTER "+process.pid+" starting workers");
    //Create a worker for each CPU core
    var numWorkers = os.cpus().length;
    for (var i = 0; i < numWorkers; i++)
        cluster.fork();
}

function worker() {
    //Put your existing code here
    console.info("WORKER "+process.pid+" starting http server");
    var httpd = http.createServer();
    //...
}

选项3:拆分结果处理

我假设回调函数执行时间长的原因是您需要处理大量的结果行,而且没有机会以更快的方式处理结果。

在这种情况下,使用process.nextTick()将处理拆分成几个块也是一个不错的主意。这些块将在几个事件循环帧中同步运行,但其他操作(如事件处理程序)可以在这些块之间执行。以下是代码的粗略(未经测试)草图:

function(err, result) {
    var s, i;
    s = 0;
    processChunk();

    // process 100 rows in one frame
    function processChunk() {
        i = s;
        s += 100;
        while (i<result.rows.length && i<s) {
            //do some stuff with result.rows[i]
            i++;
        }
        if (i<result.rows.length)
            process.nextTick(processChunk);
        else
            //go on (send the response)
    }
}

我不是100%确定,但我认为node-pg提供了一种接收查询结果的方式,不是作为一个整体,而是分成几个块。这将大大简化代码,因此可能是在这个方向上搜索的一个想法...

最终结论

我首先会使用选项2,如果新请求仍然需要等待太长时间,则另外使用选项3。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接