【问题标题】:Handling multiple parallel HTTP requests in Node.js在 Node.js 中处理多个并行 HTTP 请求
【发布时间】:2013-06-12 08:28:55
【问题描述】:

我知道 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);

【问题讨论】:

标签: node.js


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    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") 
    

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

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

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

        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                                                                                                                                                                       
        

        【讨论】:

        • 你是对的。 Chrome 正在这样做。至少对我来说,在第一个请求返回之前不会发出第二个请求。
        • 即使 chrome 通常也应该允许每个源最多 6 个并发连接。
        • 我知道。这就是为什么我很惊讶并认为它是 Node。
        • 你知道为什么 Chrome 不能同时发送 2 个请求吗?
        • 坦率地说,我不相信 OP 声称 chrome 正在按照我上面的评论序列化请求。浏览器和服务器之间的完整交互是复杂的。但是没有代码来重复这个问题,我无法进一步评论。
        猜你喜欢
        • 1970-01-01
        • 2017-12-26
        • 2018-03-02
        • 1970-01-01
        • 1970-01-01
        • 2016-03-29
        • 2023-01-30
        • 2021-05-26
        • 1970-01-01
        相关资源
        最近更新 更多