当使用非阻塞I/O服务器时,单个请求需要很长时间会发生什么?

11
使用Node.js或Eventlet或其他非阻塞服务器时,当某个请求时间较长时会发生什么情况?它会阻塞所有其他请求吗?
例如,有一个请求进来,需要200毫秒计算,由于Node.js使用单线程等原因,这将会阻塞其他请求。
这意味着每秒处理的请求数将因为实际响应计算时间的原因而显著下降。
但是,这听起来不太对,所以我想知道真正发生了什么,因为我无法想象事情是如何运作的。
6个回答

10
是否“阻塞”取决于您对“阻塞”的定义。通常,阻塞意味着您的CPU基本上处于空闲状态,但当前线程无法对其执行任何操作,因为它正在等待I/O或类似操作。在Node.js中很少发生这种情况,除非使用不推荐使用的同步I/O函数。相反,函数会快速返回,并且当它们启动的I/O任务完成时,调用回调函数并由您接管。在此期间,可以处理其他请求。
如果您在node中进行某些计算密集型操作,则在完成之前,没有其他东西能够使用CPU,但原因完全不同:CPU实际上正在忙碌。通常,当人们说“阻塞”时,他们指的不是这种情况,而是长时间的计算。
如果不涉及I/O且仅进行计算,那么200毫秒就是很长的时间。老实说,这可能不是您应该在node中做的事情。更符合node精神的解决方案是,让这种数量级的计算发生在另一个(非JavaScript)程序中,该程序由node调用,并在完成时调用您的回调函数。假设您拥有一台多核机器(或者另一个程序在另一台机器上运行),则在另一个程序进行计算的同时,node可以继续响应请求。
确实有一些情况下,集群(正如其他人所提到的)可能会有所帮助,但我怀疑您的情况并不是那种情况。集群真的是用于当您有大量小请求时,这些请求共同超过了CPU单个核心的处理能力,而不是针对每个请求需要耗费数百毫秒的情况。

2

Node.js内部所有内容都是并行运行的。然而,你自己的代码严格按照顺序运行。如果在node.js中休眠一秒钟,服务器也会休眠一秒钟。它不适用于需要大量计算的请求。I/O是并行的,你的代码通过回调函数进行I/O(所以在等待I/O时,你的代码不会运行)。

在大多数现代平台上,node.js确实使用线程进行I/O。它使用libev,在平台上最适合的地方使用线程。


1
这里有一个很好的解释(http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/),基本上就是你所说的,但解释得更详细一些。 - Joseph Yaduvanshi

1

你说得完全正确。Node.js开发人员必须意识到这一点,否则他们的应用程序将完全无法执行,如果长时间运行的代码不是异步的话。

所有需要花费“很长时间”的操作都必须以异步方式完成。


1

这基本上是正确的,至少如果您不使用新的cluster功能来平衡多个自动生成的工作进程之间的传入连接。然而,如果您使用它,大多数其他请求仍将快速完成。

编辑:工作进程是进程。


1

你可以将事件循环想象成排队支付账单的10个人。如果有人花费太多时间来支付账单(因此阻塞了事件循环),其他人只能在那里等待他们的机会..一直等待...

换句话说

由于事件循环在单个线程上运行,因此非常重要的是不要通过在回调函数或同步I/O中进行繁重的计算来阻塞其执行。在回调函数中遍历大量值/对象或执行耗时的计算会防止事件循环进一步处理队列中的其他事件。


0

以下是一些代码,可以实际看到阻塞/非阻塞的示例:

  • 通过这个例子(CPU计算时间长,无I/O操作):

    var net = require('net');
    handler = function(req, res) {
        console.log('hello');
        for (i = 0; i < 10000000000; i++) { a = i + 5;  }
    }
    net.createServer(handler).listen(80);
    

    如果在浏览器中进行两个请求,服务器控制台只会显示一个“hello”,这意味着第二个请求不能被处理,因为第一个请求阻塞了Node.js线程。

  • 如果我们做一个I/O任务(将2GB的数据写入磁盘,在我的测试中需要几秒钟,甚至在SSD上):

    http = require('http');
    fs = require('fs');
    buffer = Buffer.alloc(2*1000*1000*1000);
    first = true;
    done = false;
    
    write = function() {
        fs.writeFile('big.bin', buffer, function() { done = true; });
    }
    
    handler = function(req, res) {
        if (first) {
            first = false;
            res.end('Starting write..')
            write();      
            return;
        }
        if (done) { 
            res.end("write done."); 
        } else {  
            res.end('writing ongoing.'); 
        }
    }
    
    http.createServer(handler).listen(80);
    

    可以看到,几秒钟的I/O写入任务 write 是非阻塞的:如果在此期间进行其他请求,您将看到“writing ongoing.”!这证实了Node.js的众所周知的非阻塞IO特性


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