在Node.js中处理多个并行HTTP请求

时间:2013-06-16 02:35:27

标签: node.js

我知道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 个答案:

答案 0 :(得分:14)

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

我通过键盘尽快在4个标签中运行curl -s localhost:8080,时间戳中的秒数为:

  1. 54至58
  2. 54至58
  3. 55至59
  4. 56 to 00
  5. 这里没有问题,虽然我可以理解你怎么认为有一个问题。如果它像你的帖子那样工作,节点将完全被破坏。

    这是另一种验证方式:

    for i in 1 2 3 4 5 6 7 8 9 10; do curl -s localhost:8080 &;done                                                                                                                                                                       
    

答案 1 :(得分:3)

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

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

我不知道实现这个的正确方法是什么。但这就是它的工作方式。

答案 2 :(得分:2)

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

答案 3 :(得分: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接受。因此我收到了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)之前接受其他请求,但是它将自己保持为单线程并且不处理其他要求所有以前的完成。