10

使用 Node.js 或 eventlet 或任何其他非阻塞服务器,当给定请求需要很长时间时会发生什么,它会阻塞所有其他请求吗?

例如,一个请求进来,计算需要 200 毫秒,这将阻塞其他请求,因为例如 nodejs 使用单个线程。

这意味着您的每秒 15K 将大幅下降,因为计算给定请求的响应所需的实际时间。

但这对我来说似乎是错误的,所以我在问真正发生了什么,因为我无法想象事情是如何运作的。

4

6 回答 6

8

它是否“阻塞”取决于您对“阻塞”的定义。通常阻塞意味着您的 CPU 基本上是空闲的,但当前线程无法对其执行任何操作,因为它正在等待 I/O 等。除非您使用非推荐的同步 I/O 函数,否则这种事情不会在 node.js 中发生。相反,函数会快速返回,并且当它们开始的 I/O 任务完成时,您的回调会被调用并从那里获取。在此期间,可以处理其他请求。

如果你在节点中做一些计算量很大的事情,那么在它完成之前没有其他东西能够使用 CPU,但原因非常不同:CPU 实际上很忙。通常,这不是人们所说的“阻塞”的意思,相反,它只是一个很长的计算。

如果不涉及 I/O 并且纯粹是在做计算,那么 200 毫秒是一个很长的时间。老实说,这可能不是您应该在节点中做的事情。一个更符合节点精神的解决方案是在另一个由节点调用的(非javascript)程序中发生这种数字运算,并在完成时调用您的回调。假设您有一台多核机器(或其他程序在另一台机器上运行),节点可以在其他程序运行时继续响应请求。

在某些情况下,集群(正​​如其他人所提到的)可能会有所帮助,但我怀疑你的集群是否真的是其中之一。集群确实适用于当您有大量的小请求,这些请求加在一起超过 CPU 的单个内核可以处理的情况下,而不是针对每个请求需要数百毫秒的单个请求的情况。

于 2011-11-16T23:36:39.937 回答
2

node.js 中的所有内容都在内部并行运行。但是,您自己的代码严格按顺序运行。如果你在 node.js 中休眠一秒钟,服务器会休眠一秒钟。它不适合需要大量计算的请求。I/O 是并行的,您的代码通过回调执行 I/O(因此您的代码在等待 I/O 时不会运行)。

在大多数现代平台上,node.js为我们提供 I/O 线程。它使用libev,它使用在平台上效果最好的线程。

于 2011-11-16T20:16:39.767 回答
1

This is basically true, at least if you don't use the new cluster feature that balances incoming connections between multiple, automatically spawned workers. However, if you do use it, most other requests will still complete quickly.

Edit: Workers are processes.

于 2011-11-16T20:33:44.003 回答
1

您可以将事件循环想象为 10 个人排队支付账单。如果有人花太多时间来支付账单(从而阻塞了事件循环),其他人将不得不闲逛等待轮到他们......并等待......

换句话说

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

于 2011-11-16T21:07:00.793 回答
1

你是完全正确的。Nodejs 开发人员必须意识到这一点,否则如果长时间运行的代码不是异步的,他们的应用程序将完全没有性能。

所有需要“很长时间”的事情都需要异步完成。

于 2011-11-16T20:16:17.847 回答
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);
    

    如果您在浏览器中执行 2 个请求,hello则服务器控制台中只会显示一个,这意味着无法处理第二个请求,因为第一个请求阻塞了 Node.js 线程。

  • 如果我们改为执行 I/O 任务(在磁盘上写入 2 GB 数据,在我的测试过程中需要几秒钟,即使在 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);
    

    在这里我们可以看到 a-few-second-long-IO-writing-taskwrite 是非阻塞的:如果您同时执行其他请求,您会看到writing ongoing.!这证实了 Node.js 众所周知的non-blocking-for-IO 特性

于 2018-06-08T09:00:51.877 回答