2015-09-29 29 views
1

我想知道在将db或其他异步调用传递给工作进程时是否有任何好处。具体来说,我使用的是heroku和postgres。我已经详细阅读了node.js以及如何构建服务器,以便事件循环未被阻止,并且智能体系结构不会使传入的请求挂起超过300毫秒左右。在node.js中使用工作程序/后台进程与异步调用

说我有以下几点:

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是异步本质上是没有任何实际的好处,以创建工作进程来处理从最初的数据库调用设置的结果的处理?

回答

0

由于真正的工作(运行SQL查询)已经在另一个进程(postgres)中运行,因此在另一个进程中运行异步函数没有任何好处。基本上,面向异步/事件的设计模式是一种轻量级流程管理器,用于在流程外运行的事物。

但是,我注意到在您的评论中,回调函数中的处理确实占用了大量的CPU时间(如果确实如此)。代码的这一部分确实从另一个进程中运行中受益 - 它释放了主进程来接受传入的请求。

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

+0

嗯,所以回调/ /做一些东西会阻止主要过程? – OliverJ90

+0

只要'do stuff'部分不是异步的,那么就会阻塞。事件循环只是对'while(){switch(){}}'循环(换句话说,一个状态机)的花哨抽象 – slebetman

+0

啊,似乎我错误地认为从最初的client.query回调是异步的。 – OliverJ90

0

在你的例子中,不,你不想使用一个进程。你可能想要解决任何需要1000毫秒才能完成的任务。

旋转子流程需要时间,因为您实质上是在创建一个全新的节点环境。你这样做的原因是,如果你有一个专门协调工作的节点,或者你正在使用一个网格式发现网络,工作人员可以在新节点联机时共享工作负载。

在任何一种情况下,工作人员都可能针对可在用户正常请求管道之外处理的作业队列进行操作。 Web套接字可用于更新作业状态的用户,但除此之外,任何查询都需要作为工作状态的请求,直到结果返回。

说了这么多,你可能需要做的是解决任何需要1000毫秒的操作。您可能操作的数据集太大,也许操作可能会分裂为多个请求,也可能是您的代码遇到严重的性能下降。无论如何,我会从那里开始,而不用担心产卵过程来处理工作。


编辑:

我是在它像是看概念,但我有一个端点内嵌套的数据库查询的几个例子。我会假设他们进入和退出主流程,因为回调被解雇了? - @ OliverJ90

我不完全拿起你把东西下来,但我想我可以理解你想要完成的任务。如果API端点足够的被调用,或者是足够大的cpu-sink,但是也需要根据用户的请求(和节点中的异步)进行处理,那么我将使用微服务。

将API抽象出来以允许从postgres接收到的一组结果,并让它返回任何期望的结果。然后,您可以将其作为单独的服务启动,并立即拥有主API可以等待的工作节点,同时释放事件回调。

+0

感谢您的想法。为了增加我的例子,我在概念上对它进行了一些研究,尽管我在端点内部有几个嵌套数据库查询的例子。我会假设他们进入和退出主流程,因为回调被解雇了? – OliverJ90

+0

我不理解你的后续问题。 – Chance

+0

在这个例子中,添加第二个client.query和回调代替//做东西 – OliverJ90

0

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

选项1:派生一个子进程

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

选项2:使用Node.js的集群

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

群集解决方案也很好,因为您不必为此更改很多代码。只需编写主进程代码并以工作人员身份启动现有代码即可。

official documentation on Node.js clusters解释所有方面,如果集群非常好,所以我不会在这里详细说明。只是一个可能的主码一个简单的例子:

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()将处理分成几个块也是一个好主意。块将在多个事件循环帧中同步运行,但其他操作(如事件处理程序)可以在这些块之间执行。这里有一个粗略的(和未经测试)scetch的代码可能看起来怎么样:

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%肯定,但我认为节点-PG提供了一些办法不接收查询结果作为一个整体,但分成几块。这将简化代码很多,所以它可能是一个想法,寻找到那个方向......

最终结论

如果有新的我会在第一时间和选项3使用选项2另外,请求仍然需要等待太久。