Więc rozumiem, jak działa Node.js: ma jeden wątek nasłuchiwania, który odbiera zdarzenie, a następnie deleguje je do puli procesów roboczych. Wątek roboczy powiadamia odbiorcę po zakończeniu pracy, a odbiorca zwraca następnie odpowiedź do wywołującego.
Moje pytanie jest takie: jeśli włączę serwer HTTP w Node.js i wywołam uśpienie na jednym z moich zdarzeń trasowych (takich jak „/ test / sleep”), cały system się zatrzymuje. Nawet pojedynczy wątek słuchacza. Ale zrozumiałem, że ten kod dzieje się na puli pracowników.
Z drugiej strony, kiedy używam Mongoose do komunikacji z MongoDB, odczyty DB są kosztowną operacją we / wy. Wydaje się, że węzeł może delegować pracę do wątku i odbierać wywołanie zwrotne po jego zakończeniu; wydaje się, że czas potrzebny na załadowanie bazy danych nie blokuje systemu.
W jaki sposób Node.js decyduje się na użycie wątku puli wątków w porównaniu z wątkiem nasłuchiwania? Dlaczego nie mogę napisać kodu zdarzenia, który jest uśpiony i blokuje tylko wątek puli wątków?
Odpowiedzi:
Twoje zrozumienie, jak działa węzeł, nie jest poprawne ... ale jest to powszechne nieporozumienie, ponieważ rzeczywistość sytuacji jest w rzeczywistości dość złożona i zazwyczaj sprowadza się do zwięzłych fraz, takich jak „węzeł jest jednowątkowy”, które nadmiernie upraszczają sprawę .
Na razie zignorujemy jawne przetwarzanie wieloprocesowe / wielowątkowe za pośrednictwem klastrów i wątków dla pracowników sieci , a po prostu porozmawiamy o typowym węźle bez wątku.
Węzeł działa w pojedynczej pętli zdarzeń. Jest jednowątkowy, a dostajesz tylko ten jeden wątek. Cały skrypt javascript, który piszesz, jest wykonywany w tej pętli, a jeśli w tym kodzie nastąpi operacja blokowania, zablokuje całą pętlę i nic więcej się nie wydarzy, dopóki się nie zakończy. Jest to typowa jednowątkowa natura węzła, o której tyle się słyszy. Ale to nie jest cały obraz.
Niektóre funkcje i moduły, zwykle napisane w C / C ++, obsługują asynchroniczne operacje we / wy. Gdy wywołujesz te funkcje i metody, wewnętrznie zarządzają przekazywaniem wywołania do wątku roboczego. Na przykład, kiedy używasz
fs
modułu do żądania pliku,fs
moduł przekazuje to wywołanie do wątku roboczego, a ten pracownik czeka na odpowiedź, którą następnie przedstawia z powrotem do pętli zdarzeń, która była uruchamiana bez niego w w międzyczasie. Wszystko to jest odciągane od Ciebie, programisty węzła, a niektóre z nich są oddzielane od twórców modułów za pomocą libuv .Jak zauważył Denis Dollfus w komentarzach (z tej odpowiedzi na podobne pytanie), strategia używana przez libuv do osiągnięcia asynchronicznych operacji we / wy nie zawsze jest pulą wątków, szczególnie w przypadku
http
modułu wydaje się być inna strategia. używane w tym czasie. Dla naszych celów ważne jest tutaj przede wszystkim zwrócenie uwagi na to, jak osiągany jest kontekst asynchroniczny (przy użyciu libuv) i że pula wątków obsługiwana przez libuv jest jedną z wielu strategii oferowanych przez tę bibliotekę w celu osiągnięcia asynchroniczności.Jeśli chodzi o głównie pokrewną styczną, w tym doskonałym artykule znajduje się znacznie głębsza analiza tego, jak węzeł osiąga asynchroniczność, a także niektóre powiązane potencjalne problemy i sposoby ich rozwiązywania . Większość z nich rozwija to, co napisałem powyżej, ale dodatkowo zwraca uwagę:
UV_THREADPOOL_SIZE
zmiennej środowiskowej, o ile robisz to, zanim pula wątków zostanie wymagana i utworzona:process.env.UV_THREADPOOL_SIZE = 10;
Jeśli chcesz tradycyjnego przetwarzania wieloprocesorowego lub wielowątkowego w węźle, możesz go uzyskać za pomocą wbudowanego
cluster
modułu lub różnych innych modułów, takich jak wyżej wymienionewebworker-threads
, lub możesz to sfałszować, wdrażając jakiś sposób dzielenia pracy i ręcznie używającsetTimeout
lubsetImmediate
lubprocess.nextTick
wstrzymać pracę i kontynuować ją w późniejszej pętli, aby umożliwić ukończenie innych procesów (ale nie jest to zalecane).Pamiętaj, że jeśli piszesz długo działający / blokujący kod w javascript, prawdopodobnie popełnisz błąd. Inne języki będą działać znacznie wydajniej.
źródło
To nie jest do końca dokładne. Node.js ma tylko jeden wątek „roboczy”, który wykonuje wykonanie javascript. W węźle istnieją wątki, które obsługują przetwarzanie we / wy, ale myślenie o nich jako o „pracownikach” jest nieporozumieniem. Naprawdę jest tylko obsługa IO i kilka innych szczegółów dotyczących wewnętrznej implementacji węzła, ale jako programista nie możesz wpływać na ich zachowanie poza kilkoma różnymi parametrami, takimi jak MAX_LISTENERS.
W JavaScript nie ma mechanizmu uśpienia. Moglibyśmy omówić to bardziej konkretnie, gdybyś opublikował fragment kodu, który Twoim zdaniem oznacza „sen”. Nie ma takiej funkcji do wywołania, aby
time.sleep(30)
na przykład symulować coś takiego jak w Pythonie. Jest,setTimeout
ale to zasadniczo NIE jest sen.setTimeout
isetInterval
jawnie zwalnia , a nie blokuje pętlę zdarzeń, aby inne bity kodu mogły być wykonywane w głównym wątku wykonawczym. Jedyne, co możesz zrobić, to zapętlić procesor z obliczeniami w pamięci, co rzeczywiście spowoduje głodzenie głównego wątku wykonawczego i sprawi, że program przestanie odpowiadać.Network IO jest zawsze asynchroniczny. Koniec opowieści. Disk IO ma zarówno synchroniczne, jak i asynchroniczne interfejsy API, więc nie ma „decyzji”. node.js będzie zachowywać się zgodnie z podstawowymi funkcjami API, które wywołujesz sync w porównaniu do normalnej asynchronicznej. Na przykład:
fs.readFile
vsfs.readFileSync
. W przypadku procesów potomnych istnieją również oddzielnechild_process.exec
ichild_process.execSync
API.Podstawową zasadą jest zawsze używanie asynchronicznych interfejsów API. Prawidłowe powody używania interfejsów API synchronizacji to kod inicjujący w usłudze sieciowej, zanim zacznie ona nasłuchiwać połączeń, lub proste skrypty, które nie akceptują żądań sieciowych dotyczących narzędzi do kompilacji i tym podobnych.
źródło
fs
, o ile wiemPula wątków, jak, kiedy i kto używał:
Po pierwsze, kiedy używamy / instalujemy Node na komputerze, uruchamia on proces wśród innych procesów, który nazywa się procesem węzła w komputerze i działa, dopóki go nie zabijesz. A ten proces to nasz tak zwany pojedynczy wątek.
Tak więc mechanizm pojedynczego wątku ułatwia blokowanie aplikacji węzła, ale jest to jedna z unikalnych funkcji, które Node.js wnosi do tabeli. Tak więc, ponownie, jeśli uruchomisz aplikację węzła, będzie ona działać tylko w jednym wątku. Nieważne, czy masz 1 czy milion użytkowników jednocześnie uzyskujących dostęp do Twojej aplikacji.
Zrozummy więc dokładnie, co dzieje się w pojedynczym wątku nodejs po uruchomieniu aplikacji węzła. Najpierw program jest inicjowany, następnie wykonywany jest cały kod najwyższego poziomu, co oznacza wszystkie kody, które nie znajdują się w żadnej funkcji zwrotnej ( pamiętaj, że wszystkie kody wewnątrz wszystkich funkcji zwrotnych zostaną wykonane w pętli zdarzeń ).
Następnie cały kod modułów wykonywany, a następnie rejestrują wszystkie wywołania zwrotne, w końcu pętla zdarzeń została uruchomiona dla Twojej aplikacji.
Tak więc, jak omówiliśmy wcześniej, wszystkie funkcje wywołania zwrotnego i kody wewnątrz tych funkcji będą wykonywane w pętli zdarzeń. W pętli zdarzeń obciążenia rozkładane są w różnych fazach. W każdym razie nie będę tutaj omawiał pętli zdarzeń.
Cóż, w celu lepszego zrozumienia puli wątków, proszę, abyś wyobraził sobie, że w pętli zdarzeń kody wewnątrz jednej funkcji zwrotnej są wykonywane po zakończeniu wykonywania kodów wewnątrz innej funkcji zwrotnej, teraz, jeśli są jakieś zadania, są w rzeczywistości zbyt ciężkie. Następnie zablokowaliby nasz pojedynczy wątek nodejs. I tu właśnie pojawia się pula wątków, która jest podobna do pętli zdarzeń, dostarczana do Node.js przez bibliotekę libuv.
Tak więc pula wątków nie jest częścią samego nodejs, jest dostarczana przez libuv, aby odciążyć duże obciążenia na libuv, a libuv wykona te kody we własnych wątkach i po wykonaniu libuv zwróci wyniki do zdarzenia w pętli zdarzeń.
Pula wątków daje nam cztery dodatkowe wątki, które są całkowicie oddzielone od głównego pojedynczego wątku. W rzeczywistości możemy skonfigurować do 128 wątków.
Wszystkie te wątki razem utworzyły pulę wątków. a pętla zdarzeń może następnie automatycznie przenosić ciężkie zadania do puli wątków.
Zabawne jest to, że wszystko to dzieje się automatycznie za kulisami. To nie my, programiści, decydujemy, co trafia do puli wątków, a co nie.
Do puli wątków trafia wiele zadań, takich jak
źródło
To nieporozumienie jest po prostu różnicą między wielozadaniowością z wywłaszczaniem a wielozadaniowością opartą na współpracy ...
Sen wyłącza cały karnawał, ponieważ do wszystkich przejażdżek jest naprawdę jedna kolejka, a ty zamknąłeś bramę. Pomyśl o tym jako o „interprecie JS i kilku innych rzeczach” i zignoruj wątki ... dla ciebie jest tylko jeden wątek, ...
... więc nie blokuj tego.
źródło