Node.js działa na jednym wątku, a i tak nadąża
Klasyczny serwer daje każdemu użytkownikowi osobny wątek. Node robi wszystko w jednym i przy tysiącach połączeń bywa szybszy. Sekret nie jest skomplikowany — to kwestia tego, czym program zajmuje się przez większość czasu.
Kiedy ktoś pierwszy raz słyszy, że Node.js jest jednowątkowy, reaguje zwykle podobnie: przecież to musi być wolne. A jednak przy obsłudze wielu jednoczesnych połączeń Node często wygrywa z serwerami, które dla każdego użytkownika odpalają osobny wątek.
Kelner zamiast kucharzy
Wyobraź sobie restaurację. Model klasyczny: każdy gość dostaje własnego kelnera. Gość czyta menu przez pięć minut — kelner stoi i czeka. Stu gości to stu kelnerów, z których większość nic nie robi.
Node działa jak jeden bardzo sprawny kelner. Przyjmuje zamówienie, oddaje je do kuchni i natychmiast idzie do następnego stolika. Kiedy danie jest gotowe, kuchnia daje znak i kelner je zanosi. Jeden człowiek obsługuje całą salę, bo nigdy nie stoi bezczynnie.
Skąd bierze się ta oszczędność
Typowa aplikacja webowa spędza większość czasu na czekaniu — na odpowiedź z bazy danych, na plik z dysku, na zewnętrzne API. Samo liczenie zajmuje ułamek tego czasu.
Node zamiast czekać zapisuje sobie: „gdy baza odpowie, wykonaj tę funkcję" — i zajmuje się kolejnym żądaniem. Ta lista zadań do wykonania po nadejściu odpowiedzi to właśnie pętla zdarzeń. Wątek jest jeden, ale nie marnuje ani chwili.
Dodatkowo każdy wątek kosztuje pamięć i czas na przełączanie kontekstu. Tysiąc wątków to setki megabajtów zjedzone przez samą infrastrukturę. Tysiąc oczekujących zadań w Node to tysiąc niewielkich wpisów na liście.
Gdzie ten model przestaje działać
Skoro wątek jest jeden, to zadanie, które naprawdę liczy, blokuje wszystkich. Zmiana rozmiaru dużego zdjęcia, generowanie raportu PDF, kompresja wideo — przez te trzy sekundy żaden inny użytkownik nie zostanie obsłużony.
Node jest świetny do zadań, które głównie czekają. Do zadań, które głównie liczą, trzeba mu pomóc.
Rozwiązania są dwa i oba proste: przenieść ciężką robotę do osobnego procesu (kolejka zadań w tle) albo skorzystać z mechanizmu wątków roboczych. W typowej stronie czy sklepie problem pojawia się rzadko — bo prawie wszystko sprowadza się do odpytania bazy i złożenia odpowiedzi.
Kiedy warto po niego sięgnąć
- API i mikroserwisy — dużo krótkich żądań, mało liczenia.
- Czat, powiadomienia, aplikacje na żywo — setki otwartych połączeń praktycznie nic nie kosztują.
- Warstwa pośrednia — kiedy trzeba połączyć odpowiedzi z kilku zewnętrznych usług i podać je frontendowi w jednym kawałku.
- Jeden język na całym stosie — ten sam kod walidacji działa w przeglądarce i na serwerze.
Node nie jest szybszy dlatego, że jest magiczny. Jest szybszy dlatego, że nie każe komputerowi stać bezczynnie.
Komentarze (9)
-
Wojtek
Porównanie z kelnerem jest lepsze niż połowa dokumentacji. Wreszcie ktoś wytłumaczył pętlę zdarzeń bez rysowania kolejek i stosów.
Odpowiedz -
Wojtek
Porównanie z kelnerem jest lepsze niż połowa dokumentacji. Wreszcie ktoś wytłumaczył pętlę zdarzeń bez rysowania kolejek i stosów.
Odpowiedz -
Iwona
Ten akapit o blokowaniu wątku przy generowaniu PDF opisuje dokładnie to, co przydarzyło się nam w zeszłym miesiącu. Poszliśmy w kolejkę zadań i problem zniknął.
Odpowiedz