Node.js中的子事件顺序

时间:2014-09-15 03:38:00

标签: node.js event-loop

我有一个api,它的工作过程是这样的:

使用1秒的CPU时间做一些逻辑

等待网络IO,这个IO也需要1秒。

所以,通常这个api需要大约2秒才能响应

然后我做了一个测试。 我同时开始10个请求。 他们中的每一个都需要超过10秒的时间来回复

此测试意味着 Node将首先完成所有10个请求中所有cpu代价高昂的部分。

为什么? 为什么在一次IO完成后它不会立即响应一个请求。


感谢您的评论。我想我需要对我的担忧做一些解释。

如果请求计数不是10,如果同时有100个请求,我关心的是。 所有这些都会超时!!

如果Node立即响应子IO事件,我认为其中至少有20%不会超时。

我认为节点需要一些事件优先级机制


router.use('/test/:id', function (req, res) {
    var id = req.param('id');
    console.log('start cpu code for ' + id);
    for (var x = 0; x < 10000; x++) {
        for (var x2 = 0; x2 < 30000; x2++) {
            x2 -= 1;
            x2 += 1;
        }
    }
    console.log('cpu code over for ' + id);
    request('http://terranotifier.duapp.com/wait3sec/' + id, function (a,b,data) {
        // how can I make this code run immediately after the server response to me.
        console.log('IO over for ' + data);
        res.send('over');
    });
});

1 个答案:

答案 0 :(得分:0)

Node.js是单线程的。因此,只要您有一个长时间运行的例程,它就无法处理其他代码段。在这个例子中,有问题的代码是你的双循环,占用了大量的CPU时间。

首先要了解你所看到的内容,让我解释一下事件循环是如何工作的。

Node.js事件循环是从javascript的事件循环演变而来的,它是由Web浏览器事件循环演变而来的。 Web浏览器事件循环最初不是针对javascript实现的,而是允许逐行渲染图像。事件循环看起来有点像这样:

,-> is there anything from the network?
|      |              |
|      no            yes
|      |              |
|      |              '-----------> read network data
|      V                                     |
|  does the DOM need updating? <-------------'
|      |              |
|      no            yes
|      |              |
|      |              v
|      |         update the DOM
|      |              |
'------'--------------'

当添加javascript时,脚本处理只是插入到事件循环中:

,-> is there anything from the network?
|      |              |
|      no            yes
|      |              |
|      |              '-----------> read network data
|      V                                     |
|  any javascript to run? <------------------'
|      |              |
|      no            yes
|      |              '-----------> run javascript
|      V                                     |
|  does the DOM need updating? <-------------'
|      |              |
|      no            yes
|      |              |
|      |              v
|      |         update the DOM
|      |              |
'------'--------------'

当javascript引擎在浏览器之外运行时,就像在Node.js中一样,简单地删除了与DOM相关的部分,并且I / O变得一般化:

,-> any javascript to run?
|      |         |
|      no       yes
|      |         |
|      |         '--------> RUN JAVASCRIPT
|      V                         |
|  is there any I/O <------------'
|      |              |
|      no            yes
|      |              |
|      |              v
|      |          read I/O
|      |              |
'------'--------------'

请注意,您的所有javascript代码都在RUN JAVASCRIPT部分中执行。

那么,当您建立10个连接时,您的代码会发生什么?

connection1: node accepts your request, processes the double for loops
connection2: node is still processing the for loops, the request gets queued
connection3: node is still processing the for loops, the request gets queued
(at some point the for loop for connection 1 finishes)
node notices that connection2 is queued so connection2 gets accepted,
process the double for loops
    ...
connection10: node is still processing the for loops, the request gets queued
(at this point node is still busy processing some other for loop,
 probably for connection 7 or something)
request1: node is still processing the for loops, the request gets queued
request2: node is still processing the for loops, the request gets queued
(at some point all connections for loops finishes)
node notices that response from request1 is queued so request1 gets processed,
console.log gets printed and res.send('over') gets executed.
    ...
request10: node is busy processing some other request, request10 gets queued
(at some point request10 gets executed)

这就是为什么你看到节点花10秒钟回答10个请求。并不是请求本身很慢,而是它们的响应排在所有for循环后面,for循环首先被执行(因为我们仍然在事件循环的当前循环中)。

要解决此问题,您应该使for循环异步,以便为节点提供处理事件循环的机会。您可以在C中编写它们并使用C为每个线程运行独立的线程。或者你可以使用npm中的一个线程模块在不同的线程中运行javascript。或者您可以使用工作线程,这是一个像Node.js实现的API工作者。或者,您可以派生一组进程来执行它们。或者,如果并行性不重要,您可以使用setTimeout简单地循环它们:

router.use('/test/:id', function (req, res) {
 var id = req.param('id');
    console.log('start cpu code for ' + id);
    function async_loop (count, callback, done_callback) {
        if (count) {
            callback();
            setTimeout(function(){async_loop(count-1, callback)},1);
        }
        else if (done_callback) {
            done_callback();
        }
    }

    var outer_loop_done=0;
    var x2=0;
    async_loop(10000,function(){
        x1++;
        async_loop(30000,function(){
            x2++;
        },function() {
            if (outer_loop_done) {
                console.log('cpu code over for ' + id);
                request('http://terranotifier.duapp.com/wait3sec/' + id,
                    function (a,b,data){
                        console.log('IO over for ' + data);
                        res.send('over');
                    }
                );
            }
        });
    },function(){
        outer_loop_done = 1;
    });
});

上面的代码将尽快处理来自request()的响应,而不是等待所有async_loop执行完成而不使用线程(因此没有并行性),而只是使用事件队列优先级