21

我知道 Node 是非阻塞的,但我刚刚意识到默认行为http.listen(8000)意味着所有 HTTP 请求都一次处理一个。我知道我不应该对此感到惊讶(这就是端口的工作方式),但它确实让我非常想知道如何编写我的代码,以便我可以处理多个并行 HTTP 请求。

那么编写服务器以使其不占用端口 80 并且长时间运行的响应不会导致长请求队列的最佳方法是什么?

为了说明问题,请尝试运行下面的代码并同时在两个浏览器选项卡中加载它。

var http = require('http');
http.createServer(function (req, res) {
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.write("<p>" + new Date().toString() + ": starting response");
    setTimeout(function () {
        res.write("<p>" + new Date().toString() + ": completing response and closing connection</p>");
        res.end();
    }, 4000);
}).listen(8080);
4

4 回答 4

17

您误解了节点的工作原理。上面的代码可以接受来自成百上千个客户端的 TCP 连接,读取 HTTP 请求,然后等待你在那里设置的 4000 毫秒超时,然后发送响应。每个客户端将在大约 4000 + 几毫秒内得到响应。在那个 setTimeout 期间(以及在任何 I/O 操作期间)节点可以继续处理。这包括接受额外的 TCP 连接。我测试了你的代码,每个浏览器都在 4 秒内得到响应。第二个不需要 8s,如果你认为它是这样工作的。

curl -s localhost:8080通过键盘尽可能快地在 4 个选项卡中运行,时间戳中的秒数是:

  1. 54 至 58
  2. 54 至 58
  3. 55 至 59
  4. 56 到 00

这里没有问题,尽管我可以理解你可能认为有一个问题。如果它按照您的帖子建议工作,节点将完全损坏。

这是另一种验证方法:

for i in 1 2 3 4 5 6 7 8 9 10; do curl -s localhost:8080 &;done                                                                                                                                                                       
于 2013-06-16T02:44:06.023 回答
4

您的代码可以接受多个连接,因为该工作是在 setTimeout 调用的回调函数中完成的。

但是,如果您不是 setTimeout 而是做繁重的工作......那么 node.js 确实不会接受其他多个连接!SetTimeout 意外释放了进程,因此 node.js 可以接受其他作业,并且您的代码在其他“线程”中执行。

我不知道哪种方法是正确的实现方式。但这似乎是如何工作的。

于 2014-03-08T16:47:40.647 回答
2

浏览器阻止其他相同的请求。如果您从不同的浏览器调用它,那么这将并行工作。

于 2017-07-01T19:30:35.100 回答
0

我使用以下代码来测试请求处理

app.get('/', function(req, res) {
  console.log('time', MOMENT());  
  setTimeout( function() {
    console.log(data, '          ', MOMENT());
    res.send(data);
    data = 'changing';
  }, 50000);
  var data = 'change first';
  console.log(data);
});

由于这个请求不需要太多的处理时间,除了 50 秒的 setTimeout 和所有超时都像往常一样一起处理。

响应 3 一起请求-

time moment("2017-05-22T16:47:28.893")
change first
time moment("2017-05-22T16:47:30.981")
change first
time moment("2017-05-22T16:47:33.463")
change first
change first            moment("2017-05-22T16:48:18.923")
change first            moment("2017-05-22T16:48:20.988")
change first            moment("2017-05-22T16:48:23.466")

在此之后,我进入第二阶段......即,如果我的请求需要花费大量时间来处理同步文件或其他需要时间的事情怎么办。

app.get('/second', function(req, res) {
    console.log(data);
    if(req.headers.data === '9') {
        res.status(200);
        res.send('response from api');
    } else {
        console.log(MOMENT());
        for(i = 0; i<9999999999; i++){}
        console.log('Second MOMENT', MOMENT());
        res.status(400);
        res.send('wrong data');
    }

    var data = 'second test';
});

由于我的第一个请求仍在处理中,所以我的第二个请求没有被 Node.js 接受。因此,我得到了 2 个请求的以下响应-

undefined
moment("2017-05-22T17:43:59.159")
Second MOMENT moment("2017-05-22T17:44:40.609")
undefined
moment("2017-05-22T17:44:40.614")
Second MOMENT moment("2017-05-22T17:45:24.643") 

因此,对于所有异步函数,Node 中都有一个虚拟线程,并且 Node 在完成之前的请求之前会接受其他请求异步工作(如 fs、mysql 或调用 API),但是它将自身保持为单线程,并且直到所有请求都不会处理其他请求前面的都完成了。

于 2017-05-22T12:24:42.423 回答