Przejdź do treści

Z czego składa się Node.js?

Co sprawia, że możemy korzystać z Node.js? Czyli czym jest Node.js, NPM, V8 oraz libuv.

Trzy podstawowe elementy Node.js

Teoretycznie wystarczą trzy elementy Node.js, które sprawiają, że ten ekosystem zaczyna działać we właściwy sposób. Sprawa jednak nie jest aż tak prosta, bo mechanizmów oraz rozwiązań, które powodują, że Node.js działa, jest wiele.

Postanowiłem opisać jednak te, o których warto wiedzieć na sam początek.

Dwa z nich, są na pierwszy rzut oka niewidoczne dla nas programistów.

Ale tylko do momentu, kiedy sobie zadajemy pytanie “jak to właściwie wszystko działa?”. Mam tu na myśli konkretnie V8 oraz libuv. Warto wspomnieć, że to zaledwie dwie z kilku zależności, z których korzysta Node.js:

  • arcon
  • brotli
  • cares
  • cjs-module-lexer
  • corepack
  • googletest
  • histogram
  • icu-small
  • llhttp
  • nghttp2
  • ngtcp2
  • npm
  • openssl
  • uv
  • uvwasi
  • v8
  • zlib

Trzeci - npm - doskonale znamy, ponieważ towarzyszy nam na co dzień. I w sumie bez niego nasza praca nie należałaby do najprzyjemniejszych.

Node.js po siada implementacje swoich modułów (np. os, http, path, dns) bezpośrednio w JavaScript, można je w prosty sposób podejrzeć w repozytorium Node.

1. NPM - Node Package Manager

Wraz z instalacją Node.js otrzymujemy w komplecie NPMa. Narzędzie bez, którego praca ówczesnego JavaScriptowca (bez znaczenia czy po stronie front-endu lub back-endu) byłaby praktycznie niemożliwa…

No dobra, bardzo mocno utrudniona.

Pamiętam, gdy programując, dawno temu w PHP nie istniał tzw. Composer - powiedzmy, odpowiednik NPMa. Każdą zależność musieliśmy ściągać z internetu i umieszczać w folderze z bibliotekami ręcznie. Aktualizacja zewnętrznych zależności była dość czasochłonna, a w praktyce nikt tego nie robił.

NPM to rejestr pakietów dla ekosystemu JS, ale także narzędzie dostępne za pomocą linii poleceń (npm CLI).

Rejestr odpowiada za gromadzenie w jednym miejscu bibliotek, które używamy w projektach. Między innymi za pomocą interfejsu webowego możemy przeszukiwać jego zasoby.

Do rejestru możemy bez problemu publikować w ilości nieograniczonej, swoje własne bliblioteki - w wersji darmowej tylko o dostępie publicznym, natomiast w wersji płatnej, także prywatne, dostępne tylko na zdefiniowanych przez nas zasadach.

npm CLI jest narzędziem, dostępnym z linii poleceń, napisanym w JavaScript. Pozwala administrować nasza biblioteką, którą publikujemy w ramach rejestru oraz zarządzać zależnościami do innych bibliotek. To tę aplikację wywołujemy, wydając znanym wszystkim polecenie: npm i

2. V8 - JavaScript Engine

Silników, które wykonują kod w standardzie języka ECMAScript mamy kilkanaście. Świetnie pokazuje to zestawienie przygotowane przez Phila Eatona w artykule “Enumerating and analyzing 40+ non-V8 JavaScript implementations”. Natomiast najbardziej popularne silniki, to te wykorzystywane w przypadku przeglądarek internetowych - Chakra, SpiderMonkey oraz V8. Bez nich, żaden kod JS umieszczony na stronach internetowych, nie byłby wykonany po stronie użytkownika, a dokładnie w przeglądarce.

Podobnie jest w przypadku Node.js. Tutaj silnikiem, który dba o wykonywanie kodu, jest V8. Stworzony w 2009 roku i aktywnie rozwijany przez Google. To właśnie V8 zapewnia nam dostępne typy danych oraz funkcje JavaScript.

V8 został napisany w języku C++. Składa się z interpretera Ignition, kompilatora optymalizującego TurboFan oraz garbage collectora Orinoco.

Aby, dowiedzieć się jak wygląda proces przetwarzania kodu JS przez V8 oraz jak działa Orinoco, polecam rzucić okiem na poniższe materiały.

W przypadku Garbage Collectora warto zapoznać się artykułem ilustrującym zasadę jego działania pt. “Garbage collection in V8, an illustrated guide”.

W tym zestawieniu ważna jest również prezentacja Petera Marshalla oraz artykuł na blogu V8 pt. “Trash talk: the Orinoco garbage collector”.

3. libuv - Cross-platform asynchronous I/O

Sercem napędowym Noda, dającym m.in.: Event Loop, asynchroniczne operacje na systemie plików, pulę wątków, obsługę sygnałów czy procesy potemne - jest wieloplatformowa biblioteka libuv. Rozwijana pierwotnie ze względu na Node.js.

Architektura libuv

Architektura libuv

Architekturę libuv można rozgraniczyć na dwie podstawowe warstwy:

  • operacje wejścia/wyjścia dla sieci, plików, DNS oraz kodu użytkownika;
  • implementacje I/O dla poszczególnych systemów operacyjnych oraz pulę wątków;

Poszczególne implementacje I/O należy rozumieć jako implementacje dla systemu operacyjnego:

Każdy z systemów implementuje oraz udostępnia inne API do obsługi asynchronicznych operacji wejścia / wyjścia. Dzięki libuv otrzymujemy abstrakcję nad tym, różnymi API. Między innymi, dzięki temu sam Node.js stał się międzyplatformowym rozwiązaniem.

Dosłownie pokrótce przybliżyłem trzy najważniejsze składniki Node.js. Oczywiście temat można rozbudować o kolejne wątki. Szczególnie o Event Loop (oparty o Reactor Pattern), Single Thread, Non Blocking I/O.