简体   繁体   English

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

[英]Handling multiple parallel HTTP requests in Node.js

I know that Node is non-blocking, but I just realized that the default behaviour of http.listen(8000) means that all HTTP requests are handled one-at-a-time. 我知道Node是非阻塞的,但我刚刚意识到http.listen(8000)的默认行为意味着所有HTTP请求都是一次一个地处理。 I know I shouldn't have been surprised at this (it's how ports work), but it does make me seriously wonder how to write my code so that I can handle multiple, parallel HTTP requests. 我知道我不应该对此感到惊讶(这是端口如何工作),但它确实让我真的想知道如何编写我的代码以便我可以处理多个并行HTTP请求。

So what's the best way to write a server so that it doesn't hog port 80 and long-running responses don't result in long request queues? 那么编写服务器的最佳方法是什么,以便它不会占用端口80并且长时间运行的响应不会导致长请求队列?

To illustrate the problem, try running the code below and loading it up in two browser tabs at the same time. 要说明问题,请尝试运行下面的代码并同时将其加载到两个浏览器选项卡中。

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

You are misunderstanding how node works. 您误解了节点的工作原理。 The above code can accept TCP connections from hundreds or thousands of clients, read the HTTP requests, and then wait the 4000 ms timeout you have baked in there, and then send the responses. 上面的代码可以接受来自数百或数千个客户端的TCP连接,读取HTTP请求,然后等待你在那里烘焙的4000毫秒超时,然后发送响应。 Each client will get a response in about 4000 + a small number of milliseconds. 每个客户端将在大约4000 +几毫秒内得到响应。 During that setTimeout (and during any I/O operation) node can continue processing. 在该setTimeout期间(以及在任何I / O操作期间),节点可以继续处理。 This includes accepting additional TCP connections. 这包括接受其他TCP连接。 I tested your code and the browsers each get a response in 4s. 我测试了你的代码,每个浏览器都得到了4s的响应。 The second one does NOT take 8s, if that is how you think it works. 第二个不需要8s,如果这是你认为它的工作方式。

I ran curl -s localhost:8080 in 4 tabs as quickly as I can via the keyboard and the seconds in the timestamps are: 我通过键盘尽可能快地在4个选项卡中运行curl -s localhost:8080 ,时间戳中的秒数为:

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

There's no issue here, although I can understand how you might think there is one. 这里没有问题,虽然我可以理解你怎么认为有一个问题。 Node would be totally broken if it worked as your post suggested. 如果它像你的帖子那样工作,节点将完全被破坏。

Here's another way to verify: 这是验证的另一种方法:

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

Your code can accept multiple connections because the job is done in callback function of the setTimeout call. 您的代码可以接受多个连接,因为作业是在setTimeout调用的回调函数中完成的。

But if you instead of setTimeout do a heavy job... then it is true that node.js will not accept other multiple connections! 但是如果你代替setTimeout做了很多工作......那么node.js就不会接受其他多个连接了! SetTimeout accidentally frees the process so the node.js can accept other jobs and you code is executed in other "thread". SetTimeout意外地释放了进程,因此node.js可以接受其他作业,并且您的代码在其他“线程”中执行。

I don't know which is the correct way to implement this. 我不知道实现这个的正确方法是什么。 But this is how it seems to work. 但这就是它的工作方式。

Browser blocks the other same requests. 浏览器阻止其他相同的请求。 If you call it from different browsers then this will work parallelly. 如果您从不同的浏览器调用它,那么这将并行工作。

I used following code to test request handling 我使用以下代码来测试请求处理

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);
});

Since this request doesn't take that much processing time, except for 50 sec of setTimeout and all the time-out were processed together like usually do. 由于此请求不需要那么多处理时间,除了50秒的setTimeout和所有超时都像往常一样处理。

Response 3 request together- 响应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")

After this i moved to second phase... ie, what if my request takes so much time to process a sync file or some thing else that take time. 在此之后,我进入了第二阶段...即,如果我的请求需要花费大量时间来处理同步文件或其他需要时间的事情。

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';
});

As my first request was still in process so my second didn't get accepted by Node. 由于我的第一个请求仍在进行中,所以我的第二个请求未被Node接受。 Thus i got following response of 2 request- 因此,我得到以下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") 

Thus For all Async functions theres a virtual thread in Node and Node does accept other request before completing previous requests async work like(fs, mysql,or calling API), however it keeps it self as single thread and does not process other request until all previous ones are completed. 因此,对于所有异步函数,Node和Node中的虚拟线程在完成先前的请求异步工作(如fs,mysql或调用API)之前接受其他请求,但是它将自己保持为单线程并且不会处理其他请求直到所有以前的完成。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM