Osobiście czytałem kod źródłowy node.js i v8.
Podjąłem podobny problem, jak ty, kiedy próbowałem zrozumieć architekturę node.js w celu napisania natywnych modułów.
To, co tutaj publikuję, to moje zrozumienie node.js i może to być również trochę odbiegające od ścieżki.
Libev to pętla zdarzeń, która faktycznie działa wewnętrznie w node.js w celu wykonywania prostych operacji pętli zdarzeń. Został napisany oryginalnie dla systemów * nix. Libev zapewnia prostą, ale zoptymalizowaną pętlę zdarzeń, na której działa proces. Możesz przeczytać więcej o libev tutaj .
LibEio to biblioteka do asynchronicznego wykonywania danych wejściowych. Obsługuje deskryptory plików, procedury obsługi danych, gniazda itp. Możesz przeczytać więcej na ten temat tutaj .
LibUv to warstwa abstrakcji na szczycie libeio, libev, c-ares (dla DNS) i iocp (dla systemu Windows asynchronous-io). LibUv wykonuje, obsługuje i zarządza wszystkimi zdarzeniami io i zdarzeniami w puli zdarzeń. (w przypadku puli wątków libeio). Powinieneś sprawdzić samouczek Ryana Dahla na temat libUv. To zacznie mieć dla ciebie więcej sensu, jeśli chodzi o samo działanie libUv, a potem zrozumiesz, jak działa node.js na szczycie libuv i v8.
Aby zrozumieć tylko pętlę zdarzeń JavaScript, należy rozważyć obejrzenie tych filmów
Aby zobaczyć, jak libeio jest używane z node.js do tworzenia modułów asynchronicznych, powinieneś zobaczyć ten przykład .
Zasadniczo to, co dzieje się w node.js, polega na tym, że pętla v8 uruchamia i obsługuje wszystkie części javascript, a także moduły C ++ [gdy działają one w głównym wątku (zgodnie z oficjalną dokumentacją node.js jest jednowątkowy)]. Gdy znajduje się poza głównym wątkiem, libev i libeio obsługują go w puli wątków, a libev zapewnia interakcję z główną pętlą. Z mojego rozumienia wynika, że node.js ma 1 stałą pętlę zdarzeń: to jest pętla zdarzeń w wersji 8. Do obsługi zadań asynchronicznych w C ++ używa puli wątków [przez libeio i libev].
Na przykład:
eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);
To, co pojawia się we wszystkich modułach, jest zwykle wywołaniem funkcji Task
w puli wątków. Po zakończeniu wywołuje AfterTask
funkcję w głównym wątku. Podczas gdy Eio_REQUEST
jest to procedura obsługi żądań, która może być strukturą / obiektem, którego motywem jest zapewnienie komunikacji między pulą wątków a głównym wątkiem.
process.nextTick
- W następnej pętli wokół pętli zdarzeń wywołaj to wywołanie zwrotne. To nie jest prosty alias do setTimeout (fn, 0), jest znacznie wydajniejszy. Do której pętli zdarzeń to się odnosi? Pętla zdarzeń V8?Wygląda na to, że niektóre z omawianych podmiotów (np. Libev itp.) Straciły na znaczeniu ze względu na to, że minęło trochę czasu, ale myślę, że pytanie nadal ma duży potencjał.
Spróbuję wyjaśnić działanie modelu sterowanego zdarzeniami za pomocą abstrakcyjnego przykładu, w abstrakcyjnym środowisku UNIX, w kontekście Node, na dzień dzisiejszy.
Perspektywa programu:
Mechanizm zdarzeń powyżej jest nazywany frameworkiem pętli zdarzeń libuv AKA. Node wykorzystuje tę bibliotekę do implementacji modelu programowania sterowanego zdarzeniami.
Perspektywa węzła:
Podczas gdy większość funkcji jest obsługiwana w ten sposób, niektóre (wersje asynchroniczne) operacji na plikach są wykonywane za pomocą dodatkowych wątków, dobrze zintegrowanych z libuv. Podczas gdy operacje we / wy w sieci mogą czekać w oczekiwaniu na zdarzenie zewnętrzne, takie jak odpowiedź innego punktu końcowego z danymi itp., Operacje na plikach wymagają trochę pracy od samego węzła. Na przykład, jeśli otworzysz plik i poczekasz, aż fd będzie gotowy z danymi, to się nie stanie, ponieważ nikt tak naprawdę nie czyta! Jednocześnie, jeśli czytasz z pliku w linii w głównym wątku, może to potencjalnie blokować inne działania w programie i może powodować widoczne problemy, ponieważ operacje na plikach są bardzo powolne w porównaniu z działaniami związanymi z procesorem. Dlatego wewnętrzne wątki robocze (konfigurowane przez zmienną środowiskową UV_THREADPOOL_SIZE) są wykorzystywane do operacji na plikach,
Mam nadzieję że to pomoże.
źródło
Wprowadzenie do libuv
Również jedno zdjęcie, które opisuje pętlę zdarzeń w Node.js autorstwa @ BusyRich
Aktualizacja 05/09/2017
Zgodnie z tym dokumentem pętla zdarzeń Node.js ,
Poniższy diagram przedstawia uproszczony przegląd kolejności operacji w pętli zdarzeń.
uwaga: każde pole będzie określane jako „faza” pętli zdarzeń.
Przegląd faz
setTimeout()
isetInterval()
.setImmediate()
.setImmediate()
tutaj wywoływane są callbacki.socket.on('close', ...)
.Pomiędzy każdym uruchomieniem pętli zdarzeń Node.js sprawdza, czy oczekuje na asynchroniczne we / wy lub zegary i zamyka czysto, jeśli ich nie ma.
źródło
In the node-v0.9.0 version of libuv libev was removed
”, ale nie ma na ten temat opisu w nodejschangelog
. github.com/nodejs/node/blob/master/CHANGELOG.md . A jeśli libev zostanie usunięte, to w jaki sposób asynchroniczne operacje wejścia / wyjścia są wykonywane w nodejs?W architekturze NodeJs istnieje jedna pętla zdarzeń.
Model pętli zdarzeń Node.js.
Aplikacje węzłów działają w modelu opartym na zdarzeniach jednowątkowych. Jednak Node implementuje pulę wątków w tle, aby można było wykonać pracę.
Node.js dodaje pracę do kolejki zdarzeń, a następnie odbiera ją w pojedynczym wątku z uruchomioną pętlą zdarzeń. Pętla zdarzeń przechwytuje najwyższy element w kolejce zdarzeń, wykonuje go, a następnie przechwytuje następny element.
Podczas wykonywania kodu, który jest dłuższy lub ma blokujące operacje we / wy, zamiast wywoływać funkcję bezpośrednio, dodaje funkcję do kolejki zdarzeń wraz z wywołaniem zwrotnym, które zostanie wykonane po zakończeniu funkcji. Po wykonaniu wszystkich zdarzeń w kolejce zdarzeń Node.js aplikacja Node.js zostaje zakończona.
Pętla zdarzeń zaczyna powodować problemy, gdy nasze funkcje aplikacji blokują się na I / O.
Node.js używa wywołań zwrotnych zdarzeń, aby uniknąć konieczności czekania na blokowanie operacji we / wy. Dlatego wszelkie żądania, które wykonują blokujące operacje we / wy, są wykonywane w innym wątku w tle.
Gdy zdarzenie blokujące operacje we / wy jest pobierane z kolejki zdarzeń, Node.js pobiera wątek z puli wątków i wykonuje tam funkcję zamiast w głównym wątku pętli zdarzeń. Zapobiega to wstrzymywaniu przez blokujące we / wy pozostałych zdarzeń w kolejce zdarzeń.
źródło
Libuv udostępnia tylko jedną pętlę zdarzeń, V8 to tylko silnik wykonawczy JS.
źródło
Jako początkujący javascript miałem te same wątpliwości, czy NodeJS zawiera 2 pętle zdarzeń? Po długich badaniach i dyskusjach z jednym z współautorów V8 otrzymałem następujące koncepcje.
źródło
pbkdf2
Funkcja ma wdrożenie JavaScript ale faktycznie delegaci wszystkich do zrobienia w C ++ stronie.zasób: https://github.com/nodejs/node/blob/master/src/node_crypto.cc
Moduł Libuv ma inną odpowiedzialność, która jest istotna dla niektórych bardzo szczególnych funkcji w bibliotece standardowej.
W przypadku niektórych wywołań funkcji biblioteki standardowej strona Node C ++ i Libuv decydują się całkowicie na wykonanie kosztownych obliczeń poza pętlą zdarzeń.
Zamiast tego używają czegoś, co nazywa się pulą wątków, pula wątków to seria czterech wątków, których można używać do wykonywania zadań wymagających dużej mocy obliczeniowej, takich jak
pbkdf2
funkcja.Domyślnie Libuv tworzy 4 wątki w tej puli wątków.
Oprócz wątków używanych w pętli zdarzeń istnieją cztery inne wątki, których można użyć do odciążenia kosztownych obliczeń, które muszą być wykonywane w naszej aplikacji.
Wiele funkcji zawartych w standardowej bibliotece Node automatycznie korzysta z tej puli wątków.
pbkdf2
Funkcja jest jednym z nich.Obecność tej puli wątków jest bardzo znacząca.
Tak więc Node nie jest naprawdę jednowątkowy, ponieważ istnieją inne wątki, których Node używa do wykonywania niektórych zadań wymagających dużej mocy obliczeniowej.
Gdyby pula zdarzeń była odpowiedzialna za wykonanie kosztownego obliczeniowo zadania, nasza aplikacja Node nie mogła zrobić nic innego.
Nasz procesor uruchamia wszystkie instrukcje w wątku jeden po drugim.
Korzystając z puli wątków, możemy robić inne rzeczy wewnątrz pętli zdarzeń podczas wykonywania obliczeń.
źródło