2013-06-16 24 views
18

Wiem, że węzeł nie jest blokowany, ale właśnie zdałem sobie sprawę, że domyślne zachowanie http.listen(8000) oznacza, że ​​wszystkie żądania HTTP są obsługiwane pojedynczo. Wiem, że nie powinienem był być tym zaskoczony (tak działają porty), ale naprawdę zastanawiam się, jak napisać mój kod, aby móc obsłużyć wiele równoległych żądań HTTP.Obsługa wielu równoległych żądań HTTP w Node.js

Jaki jest najlepszy sposób napisania serwera, aby nie hog port 80, a długotrwałe odpowiedzi nie powodowały długich kolejek żądań?

Aby zilustrować problem, spróbuj uruchomić poniższy kod i załadować go na dwóch kartach przeglądarki jednocześnie.

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

http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/ – Paul

Odpowiedz

14

Nie można zrozumieć, jak działa węzeł. Powyższy kod może akceptować połączenia TCP od setek lub tysięcy klientów, odczytywać żądania HTTP, a następnie czekać na limit czasu 4000 ms, w którym się wypiekł, a następnie wysłać odpowiedzi. Każdy klient otrzyma odpowiedź w około 4000 + małą liczbę milisekund. Podczas tego setTimeout (i podczas każdej operacji I/O) węzeł może kontynuować przetwarzanie. Obejmuje to akceptowanie dodatkowych połączeń TCP. Przetestowałem Twój kod, a każda przeglądarka otrzymuje odpowiedź w 4s. Drugi NIE bierze 8s, jeśli tak myślisz, że to działa.

uruchomiony curl -s localhost:8080 w 4 karty tak szybko jak to możliwe, za pomocą klawiatury i sekund w znaczniki czasu są:

  1. 54 do 58
  2. 54 do 58
  3. 55 do 59
  4. 56 do 00

Nie ma tu problemu, chociaż rozumiem, jak możesz myśleć, że taki jest. Węzeł byłby całkowicie uszkodzony, gdyby działał jak sugerował twój post.

Oto kolejny sposób na sprawdzenie:

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

Jesteś poprawne. To Chrome to robi. Przynajmniej dla mnie drugie żądanie nie zostanie wydane, dopóki nie zostanie zwrócona pierwsza. – Andrew

+0

Nawet chrom powinny normalnie pozwalają maksymalnie 6 równoczesnych połączeń na pochodzenie. –

+1

Wiem. Dlatego byłem zaskoczony i założyłem, że to Node. – Andrew

3

Twój kod może zaakceptować wiele połączeń, ponieważ praca jest wykonywana w funkcji wywołania zwrotnego wywołania setTimeout.

Ale jeśli zamiast setTimeout wykonasz ciężką pracę ... to prawdą jest, że node.js nie zaakceptuje innych połączeń wielokrotnych! SetTimeout przypadkowo zwalnia proces, aby plik node.js mógł przyjmować inne zadania, a kod jest wykonywany w innym "wątku".

Nie wiem, jaki jest właściwy sposób realizacji tego. Ale tak to wygląda.

0

Użyłem następujący kod do testowania żądania obsługi

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

Ponieważ wniosek ten nie bierze dużo czasu przetwarzania, z wyjątkiem 50 sek setTimeout i cały czas-out były przetwarzane razem jak zwykle zrobić.

Response 3 prośba jednocześnie:

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

po tym przeniósł się do drugiego etapu, czyli ... co jeśli moja prośba zajmuje tak dużo czasu na przetworzenie synchronizacji pliku lub jakąś rzecz innego, co wymaga czasu.

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

Ponieważ moja pierwsza prośba była nadal w toku, więc moja druga nie została zaakceptowana przez węzeł. Tak więc dostałem po odpowiedzi 2 request-

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

Zatem dla wszystkich funkcji ASYNC tam wirtualny wątek w węzeł i węzeł ma przyjąć inny wniosek przed ukończenie poprzednich żądań asynchronicznych pracować jak (FS, mysql, lub wywołanie API) jednak zachowuje ją samodzielnie jako pojedynczy wątek i nie przetwarza innego żądania, dopóki wszystkie poprzednie nie zostaną zakończone.

1

przeglądarkowe blokuje inne samych wniosków. Jeśli zadzwonisz do niego z różnych przeglądarek, to będzie działać równolegle.

Powiązane problemy