Co to jest Node.js? Wyjaśnienie środowiska wykonawczego JavaScript

Skalowalność, opóźnienia i przepustowość to kluczowe wskaźniki wydajności serwerów WWW. Utrzymanie niskiego opóźnienia i wysokiej przepustowości podczas skalowania w górę i w dół nie jest łatwe. Node.js to środowisko wykonawcze JavaScript, które osiąga małe opóźnienia i wysoką przepustowość, przyjmując podejście „nieblokujące” do obsługi żądań. Innymi słowy, Node.js nie marnuje czasu ani zasobów na oczekiwanie na zwrot żądań we / wy.

W tradycyjnym podejściu do tworzenia serwerów WWW, na każde żądanie przychodzącego lub połączenie z serwerem ikra nowy wątek wykonania lub nawet rozwidla nowego procesu , aby rozpatrzyć wniosek i wysłać odpowiedź. Koncepcyjnie ma to sens, ale w praktyce wiąże się z dużym obciążeniem.

Podczas gdy tworzenie wątków wiąże się z mniejszym obciążeniem pamięci i procesora niż procesy rozwidlające , nadal może być nieefektywne. Obecność dużej liczby wątków może spowodować, że mocno obciążony system będzie poświęcał cenne cykle na planowanie wątków i przełączanie kontekstu, co zwiększa opóźnienia i nakłada ograniczenia na skalowalność i przepustowość.

Node.js przyjmuje inne podejście. Uruchamia jednowątkową pętlę zdarzeń zarejestrowaną w systemie w celu obsługi połączeń, a każde nowe połączenie powoduje uruchomienie funkcji zwrotnej JavaScript . Funkcja wywołania zwrotnego może obsługiwać żądania z nieblokującymi wywołaniami we / wy, aw razie potrzeby może tworzyć wątki z puli w celu wykonywania operacji blokujących lub intensywnie wykorzystujących procesor oraz równoważenia obciążenia między rdzeniami procesora. Podejście Node do skalowania z funkcjami zwrotnymi wymaga mniej pamięci do obsługi większej liczby połączeń niż większość konkurencyjnych architektur, które skalują się z wątkami, w tym Apache HTTP Server, różne serwery aplikacji Java, IIS i ASP.NET oraz Ruby on Rails.

Node.js okazuje się całkiem przydatny w aplikacjach desktopowych oprócz serwerów. Należy również pamiętać, że aplikacje Node nie są ograniczone do czystego JavaScript. Możesz użyć dowolnego języka, który transponuje się do JavaScript, na przykład TypeScript i CoffeeScript. Node.js zawiera silnik JavaScript Google Chrome V8, który obsługuje składnię ECMAScript 2015 (ES6) bez konieczności stosowania transpilera ES6-do-ES5, takiego jak Babel.

Wiele narzędzi Node pochodzi z dużej biblioteki pakietów, do której można uzyskać dostęp z npmpolecenia. NPM, menedżer pakietów Node, jest częścią standardowej instalacji Node.js, chociaż ma własną stronę internetową.

Trochę historii JavaScript

W 1995 roku Brendan Eich, ówczesny podwykonawca firmy Netscape, stworzył język JavaScript, który ma działać w przeglądarkach internetowych - w ciągu 10 dni, jak głosi historia. Początkowo JavaScript miał umożliwić animacje i inne manipulacje modelem obiektowym dokumentu przeglądarki (DOM). Wkrótce potem wprowadzono wersję JavaScript dla Netscape Enterprise Server.

Nazwa JavaScript została wybrana do celów marketingowych, ponieważ język Java firmy Sun był wówczas szeroko rozreklamowany. W rzeczywistości język JavaScript został oparty głównie na językach Scheme i Self, z powierzchowną semantyką podobną do Java.

Początkowo wielu programistów odrzuciło JavaScript jako bezużyteczny do „prawdziwej pracy”, ponieważ jego interpreter działał o rząd wielkości wolniej niż języki kompilowane. Zmieniło się to, ponieważ kilka prac badawczych mających na celu przyspieszenie JavaScript zaczęło przynosić owoce. Co najważniejsze, silnik JavaScript Google Chrome V8 typu open source, który wykonuje kompilację dokładnie na czas, wstawianie i dynamiczną optymalizację kodu, może w rzeczywistości przewyższać kod C ++ w przypadku niektórych ładowań i przewyższa Pythona w większości przypadków użycia.

Oparta na JavaScript platforma Node.js została wprowadzona w 2009 roku przez Ryana Dahla dla systemów Linux i MacOS jako bardziej skalowalna alternatywa dla serwera Apache HTTP. NPM, napisany przez Isaaca Schluetera, został wydany w 2010 roku. Node.js w wersji dla Windows zadebiutował w 2011 roku.

Joyent był właścicielem, zarządzał i wspierał wysiłki na rzecz rozwoju Node.js przez wiele lat. W 2015 roku projekt Node.js został przekazany Fundacji Node.js i stał się zarządzany przez techniczny komitet sterujący fundacji. Node.js został również przyjęty jako projekt współpracy Linux Foundation. W 2019 roku Node.js Foundation i JS Foundation połączyły się, tworząc OpenJS Foundation.

Podstawowa architektura Node.js.

Na wysokim poziomie Node.js łączy silnik JavaScript Google V8, jednowątkową nieblokującą pętlę zdarzeń i niskopoziomowy interfejs I / O API. Uproszczony przykładowy kod pokazany poniżej ilustruje podstawowy wzorzec serwera HTTP, wykorzystujący funkcje strzałkowe ES6 (anonimowe funkcje Lambda zadeklarowane przy użyciu operatora grubej strzałki =>) dla wywołań zwrotnych.

Początek kodu ładuje moduł HTTP, ustawia hostnamezmienną serwera na localhost(127.0.0.1) i ustawia portzmienną na 3000. Następnie tworzy serwer i funkcję zwrotną, w tym przypadku funkcję grubej strzałki, która zawsze zwraca to samo odpowiedź na każde żądanie: statusCode200 (powodzenie), zwykły tekst typu treści i odpowiedź tekstowa w formacie ”Hello World\n”. Na koniec informuje serwer, aby nasłuchiwał na localhostporcie 3000 (przez gniazdo) i definiuje wywołanie zwrotne w celu wydrukowania komunikatu dziennika na konsoli, gdy serwer zacznie nasłuchiwać. Jeśli uruchomisz ten kod w terminalu lub konsoli za pomocą nodepolecenia, a następnie przejdziesz do localhost: 3000 za pomocą dowolnej przeglądarki internetowej na tym samym komputerze, w przeglądarce zobaczysz „Hello World”. Aby zatrzymać serwer, naciśnij Control-C w oknie terminala.

Należy zauważyć, że każde wywołanie wykonane w tym przykładzie jest asynchroniczne i nieblokujące. Funkcje zwrotne są wywoływane w odpowiedzi na zdarzenia. createServerZwrotna obsługuje zdarzenie żądanie klienta i zwraca odpowiedź. listenZwrotna obsługuje listeningzdarzenia.

Biblioteka Node.js.

Jak widać po lewej stronie na poniższym rysunku, Node.js ma szeroki zakres funkcjonalności w swojej bibliotece. Moduł HTTP, którego użyliśmy we wcześniejszym przykładowym kodzie, zawiera zarówno klasy klienta, jak i serwera, co widać po prawej stronie rysunku. Funkcjonalność serwera HTTPS korzystająca z TLS lub SSL znajduje się w osobnym module.

Jednym nieodłącznym problemem związanym z jednowątkową pętlą zdarzeń jest brak skalowania pionowego, ponieważ wątek pętli zdarzeń będzie używał tylko jednego rdzenia procesora. W międzyczasie nowoczesne chipy CPU często mają osiem lub więcej rdzeni, a nowoczesne szafy serwerowe mają często wiele procesorów. Aplikacja jednowątkowa nie będzie w pełni wykorzystywać 24-rdzeniowych rdzeni w solidnej szafie serwerowej.

Możesz to naprawić, chociaż wymaga to dodatkowego programowania. Po pierwsze, Node.js może tworzyć procesy potomne i utrzymywać potoki między rodzicem a dziećmi, podobnie jak popen(3)działa wywołanie systemowe , używając child_process.spawn() i powiązanych metod.

Moduł klastra jest jeszcze bardziej interesujący niż moduł potomny procesu do tworzenia skalowalnych serwerów. Ta cluster.fork()metoda odradza procesy robocze, które współużytkują porty serwera rodzica, używając opcji child_process.spawn()pod osłonami. Serwer główny klastra rozdziela połączenia przychodzące między swoich pracowników, używając domyślnie algorytmu działania okrężnego, który jest wrażliwy na obciążenia procesów roboczych.

Zauważ, że Node.js nie zapewnia logiki routingu. Jeśli chcesz zachować stan między połączeniami w klastrze, musisz przechowywać sesje i obiekty logowania w innym miejscu niż robocza pamięć RAM.

Ekosystem pakietów Node.js.

Rejestr NPM zawiera ponad 1,2 miliona pakietów bezpłatnego kodu Node.js wielokrotnego użytku, co czyni go największym rejestrem oprogramowania na świecie. Należy zauważyć, że większość pakietów NPM (zasadniczo foldery lub elementy rejestru NPM zawierające program opisany w pliku package.json) zawiera wiele modułów (programów ładowanych za pomocą requireinstrukcji). Łatwo pomylić te dwa terminy, ale w tym kontekście mają one określone znaczenie i nie należy ich zamieniać.

NPM może zarządzać pakietami, które są lokalnymi zależnościami określonego projektu, a także globalnie zainstalowanymi narzędziami JavaScript. Gdy NPM jest używany jako menedżer zależności dla projektu lokalnego, może zainstalować za pomocą jednego polecenia wszystkie zależności projektu za pośrednictwem pliku package.json. W przypadku instalacji globalnych NPM często wymaga uprawnień systemowych (sudo).

Nie musisz używać wiersza poleceń NPM, aby uzyskać dostęp do publicznego rejestru NPM. Inne menedżery pakietów, takie jak Facebook's Yarn, oferują alternatywne doświadczenia po stronie klienta. Możesz także wyszukiwać i przeglądać pakiety za pomocą witryny NPM.

Dlaczego miałbyś chcieć użyć pakietu NPM? W wielu przypadkach zainstalowanie pakietu za pomocą wiersza poleceń NPM jest najszybszym i najwygodniejszym sposobem na uzyskanie najnowszej stabilnej wersji modułu działającego w twoim środowisku i zazwyczaj jest mniej pracochłonne niż klonowanie repozytorium źródłowego i budowanie instalacji z repozytorium. Jeśli nie chcesz najnowszej wersji, możesz określić numer wersji w NPM, co jest szczególnie przydatne, gdy jeden pakiet zależy od innego pakietu i może zepsuć się z nowszą wersją zależności.

Na przykład struktura Express, minimalna i elastyczna struktura aplikacji sieci Web Node.js, zapewnia solidny zestaw funkcji do tworzenia jedno- i wielostronicowych oraz hybrydowych aplikacji internetowych. Podczas gdy łatwe do klonowania repozytorium kodu Expresscode znajduje się pod adresem //github.com/expressjs/express, a dokumentacja Express znajduje się pod adresem //expressjs.com/, szybkim sposobem rozpoczęcia korzystania z Express jest zainstalowanie go w już zainicjowanym lokalnym działającym programie katalog za pomocą npmpolecenia, na przykład:

$ npm install express - zapisz

—saveOpcja, która jest faktycznie domyślnie w KMP 5.0 i później opowiada menedżera pakietów dodać moduł Express do listy zależności w pliku package.json po instalacji.

Innym szybkim sposobem rozpoczęcia korzystania z Express jest globalna instalacja generatora plików wykonywalnych ,express(1) a następnie użycie go do lokalnego utworzenia aplikacji w nowym folderze roboczym:

$ npm install -g express-generator @ 4

$ express / tmp / foo && cd / tmp / foo

Po osiągnięciu tego, możesz użyć NPM do zainstalowania wszystkich niezbędnych zależności i uruchomienia serwera na podstawie zawartości pliku package.json utworzonego przez generator:

$ npm install

$ npm start

Trudno jest wybrać najważniejsze z ponad miliona pakietów w NPM, ale wyróżnia się kilka kategorii. Express jest najstarszym i najbardziej znanym przykładem frameworków Node.js. Inną dużą kategorią w repozytorium NPM są narzędzia programistyczne JavaScript, w tym browserify, pakiet modułów; bower, menedżer pakietów przeglądarki; grunt, uruchamiający zadania JavaScript; i gulp, system kompilacji strumieniowej. Wreszcie, ważną kategorią dla deweloperów Node.js dla przedsiębiorstw są klienci baz danych, których jest ponad 8 000, w tym popularne moduły, takie jak redis, mongoose, firebase i pg, klient PostgreSQL.

Podsumowując, Node.js to międzyplatformowe środowisko wykonawcze JavaScript dla serwerów i aplikacji. Jest zbudowany na jednowątkowej, nieblokującej pętli zdarzeń, silniku JavaScript przeglądarki Google Chrome V8 i niskopoziomowym interfejsie API we / wy. Różne techniki, w tym moduł klastra, umożliwiają aplikacjom Node.js skalowanie poza pojedynczy rdzeń procesora. Poza podstawową funkcjonalnością, Node.js zainspirował ekosystem ponad miliona pakietów, które są rejestrowane i wersjonowane w repozytorium NPM i mogą być instalowane za pomocą wiersza poleceń NPM lub alternatywy, takiej jak Yarn.