【问题标题】:Using worker/background processes in node.js vs async call在 node.js 与异步调用中使用工作进程/后台进程
【发布时间】:2015-12-26 08:46:48
【问题描述】:

我想知道将 db 或其他异步调用传递给一个或多个工作进程是否有任何好处。具体来说,我使用的是heroku和postgres。我已经阅读了有关 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 本质上是异步的,那么创建一个工作进程来处理来自初始 db 调用的结果集的处理有什么真正的好处吗?

【问题讨论】:

    标签: javascript node.js postgresql heroku


    【解决方案1】:

    从单独的进程调用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() 将处理分成几个块也是一个好主意。这些块将在几个事件循环帧中同步运行,但可以在这些块之间执行其他操作(如事件处理程序)。这是代码的粗略(未经测试)草图:

    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。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 嗯,所以回调 //do some stuff 会阻塞主进程?
      • 只要do stuff 部分不是异步的,那么是的,它就会阻塞。事件循环只是对while(){switch(){}} 循环(换句话说,状态机)的抽象抽象
      • 啊,我认为初始 client.query 中的所有内容(包括回调)都是异步的似乎是不正确的。
      猜你喜欢
      • 2011-06-13
      • 1970-01-01
      • 1970-01-01
      • 2011-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-29
      相关资源
      最近更新 更多