Ten artykuł pochodzi od Tomislava Capana, konsultanta technicznego i entuzjasty Node.js. Tomislav pierwotnie opublikował to w sierpniu 2013 r. na blogu Toptal – oryginalny post można znaleźć tutaj; blog został nieco zaktualizowany. Poniższy temat jest oparty na opinii i doświadczeniach tego autora.
Rosnąca popularność JavaScriptu przyniosła ze sobą wiele zmian, a oblicze tworzenia stron internetowych jest dziś dramatycznie różne. Rzeczy, które możemy obecnie robić w sieci z JavaScriptem działającym na serwerze, jak również w przeglądarce, były trudne do wyobrażenia zaledwie kilka lat temu, lub były zamknięte w środowiskach piaskownicowych, takich jak Flash lub aplety Java.
Przed zagłębieniem się w Node.js, możesz chcieć przeczytać o korzyściach płynących z używania JavaScript w całym stosie, który ujednolica język i format danych (JSON), pozwalając na optymalne ponowne wykorzystanie zasobów deweloperskich. Ponieważ jest to bardziej zaleta JavaScriptu niż konkretnie Node.js, nie będziemy jej tutaj zbytnio omawiać. Ale jest to kluczowa zaleta włączenia Node.js do Twojego stosu.
Node.js jest środowiskiem uruchomieniowym JavaScript zbudowanym na silniku V8 JavaScript w Chrome. Warto zauważyć, że Ryan Dahl, twórca Node.js, miał na celu stworzenie stron internetowych w czasie rzeczywistym z funkcją push, „zainspirowanych aplikacjami takimi jak Gmail”. W Node.js dał programistom narzędzie do pracy w paradygmacie non-blocking, event-driven I/O.
Jednym zdaniem: Node.js błyszczy w aplikacjach internetowych działających w czasie rzeczywistym, wykorzystujących technologię push przez websockets. Co jest w tym takiego rewolucyjnego? Otóż, po ponad 20 latach funkcjonowania stateless-web opartego na paradygmacie stateless request-response, w końcu mamy aplikacje webowe z dwukierunkowymi połączeniami w czasie rzeczywistym, gdzie zarówno klient jak i serwer mogą inicjować komunikację, co pozwala im na swobodną wymianę danych.
Jest to wyraźny kontrast z typowym paradygmatem web response, gdzie klient zawsze inicjuje komunikację. Dodatkowo, wszystko to jest oparte na otwartym stosie sieciowym (HTML, CSS i JS) działającym na standardowym porcie 80.
Można by argumentować, że mieliśmy to od lat w formie Flasha i apletów Java – ale w rzeczywistości były to tylko środowiska piaskownicowe używające sieci jako protokołu transportowego do dostarczenia do klienta. Dodatkowo, były one uruchamiane w izolacji i często działały na niestandardowych portach, co mogło wymagać dodatkowych uprawnień i tym podobnych.
Wraz ze wszystkimi swoimi zaletami, Node.js odgrywa teraz krytyczną rolę w stosie technologicznym wielu wysoko postawionych firm, które polegają na jego unikalnych korzyściach. Fundacja Node.js skonsolidowała wszystkie najlepsze przemyślenia na temat tego, dlaczego przedsiębiorstwa powinny rozważyć Node.js w krótkiej prezentacji, którą można znaleźć na stronie Node.js Foundation’s Case Studies.
W tym poście omówię nie tylko to, jak te zalety są osiągane, ale także dlaczego możesz chcieć używać Node.js – i dlaczego nie – używając niektórych klasycznych modeli aplikacji internetowych jako przykładów.
- Jak to działa?
- npm: The Node Package Manager
- Gdzie Node.js powinien być używany
- API NA DACHU OBIEKTOWYM
- QUEUED INPUTS
- STREAMING DANYCH
- PROXY
- BROKERAGE – STOCK TRADER’S DASHBOARD
- APPLICATION MONITORING DASHBOARD
- SYSTEM MONITORING DASHBOARD
- SERVER-SIDE WEB APPLICATIONS
- SERVER-SIDE WEB APPLICATION WITH A RELATIONAL DATABASE BEHIND
- HEAVY SERVER-SIDE COMPUTATION/PROCESSING
- Wniosek
Jak to działa?
Główna idea Node.js: użyj nieblokującego, sterowanego zdarzeniami wejścia/wyjścia, aby pozostać lekkim i wydajnym w obliczu aplikacji intensywnie przetwarzających dane w czasie rzeczywistym, które działają na rozproszonych urządzeniach.
To dużo powiedziane.
To, co to naprawdę oznacza, to fakt, że Node.js nie jest nową platformą, która zdominuje świat tworzenia stron internetowych. Zamiast tego, jest to platforma, która wypełnia konkretną potrzebę. I zrozumienie tego jest absolutnie niezbędne. Zdecydowanie nie chcesz używać Node.js do operacji intensywnie wykorzystujących procesor; w rzeczywistości, używanie go do ciężkich obliczeń unieważni prawie wszystkie jego zalety. Gdzie Node.js naprawdę błyszczy jest w budowaniu szybkich, skalowalnych aplikacji sieciowych, ponieważ jest w stanie obsłużyć ogromną liczbę jednoczesnych połączeń z wysoką przepustowością, co równa się wysokiej skalowalności.
Jak to działa pod maską jest całkiem interesujące. W porównaniu z tradycyjnymi technikami web-serwisu, gdzie każde połączenie (żądanie) powoduje powstanie nowego wątku, zajmującego systemową pamięć RAM i ostatecznie maxing-out na ilość dostępnej pamięci RAM, Node.js działa na pojedynczym wątku, używając nieblokujących wywołań I/O, co pozwala mu obsługiwać dziesiątki tysięcy współbieżnych połączeń (utrzymywanych w pętli zdarzeń).
To jest najprostszy przykład. Aby uzyskać bardziej solidne rozwiązanie, można użyć prostego cache’u opartego na sklepie Redis. Lub w jeszcze bardziej zaawansowanym rozwiązaniu, kolejka komunikatów do obsługi routingu wiadomości do klientów i bardziej solidny mechanizm dostarczania, który może obejmować tymczasowe utraty połączenia lub przechowywanie wiadomości dla zarejestrowanych klientów, gdy są one w trybie offline. Ale niezależnie od ulepszeń, które wprowadzisz, Node.js nadal będzie działał w oparciu o te same podstawowe zasady: reagowanie na zdarzenia, obsługa wielu współbieżnych połączeń i utrzymywanie płynności wrażeń użytkownika.
API NA DACHU OBIEKTOWYM
Pomimo że Node.js naprawdę błyszczy w aplikacjach czasu rzeczywistego, to całkiem naturalnie nadaje się do eksponowania danych z obiektowych baz danych (np. MongoDB). Dane przechowywane w JSON pozwalają Node.js działać bez niedopasowania impedancji i konwersji danych.
Na przykład, jeśli używasz Rails, konwertowałbyś z JSON do modeli binarnych, a następnie eksponowałbyś je z powrotem jako JSON przez HTTP, gdy dane są konsumowane przez React.js, Angular.js, itp. lub nawet zwykłe wywołania jQuery AJAX. Dzięki Node.js możesz po prostu odsłonić swoje obiekty JSON za pomocą interfejsu API REST, aby klient mógł je skonsumować. Dodatkowo, nie musisz się martwić o konwersję między JSON a czymkolwiek innym podczas odczytu lub zapisu z bazy danych (jeśli używasz MongoDB). Podsumowując, możesz uniknąć potrzeby wielokrotnej konwersji, używając jednolitego formatu serializacji danych w kliencie, serwerze i bazie danych.
QUEUED INPUTS
Jeśli otrzymujesz dużą ilość równoczesnych danych, twoja baza danych może stać się wąskim gardłem. Jak pokazano powyżej, Node.js może łatwo obsłużyć współbieżne połączenia samodzielnie. Ale ponieważ dostęp do bazy danych jest operacją blokującą (w tym przypadku), napotykamy na problemy. Rozwiązaniem jest potwierdzenie zachowania klienta, zanim dane zostaną naprawdę zapisane do bazy danych.
Dzięki takiemu podejściu system zachowuje swoją responsywność przy dużym obciążeniu, co jest szczególnie przydatne, gdy klient nie potrzebuje solidnego potwierdzenia udanego zapisu danych. Typowe przykłady obejmują: rejestrowanie lub zapisywanie danych śledzących użytkownika, przetwarzanych w partiach i niewykorzystywanych do późniejszego czasu; a także operacje, które nie muszą być odzwierciedlane natychmiast (jak aktualizacja liczby „polubień” na Facebooku), gdzie ewentualna spójność (tak często stosowana w świecie NoSQL) jest akceptowalna.
Dane są kolejkowane przez jakiś rodzaj buforowania lub infrastruktury kolejkowania komunikatów (MQ) (np., RabbitMQ, ZeroMQ) i trawione przez oddzielny proces wsadowego zapisu do bazy danych, lub usługi backendu intensywnie przetwarzające dane obliczeniowe, napisane w lepiej działającej platformie do takich zadań. Podobne zachowanie może być zaimplementowane w innych językach/frameworkach, ale nie na tym samym sprzęcie, z tą samą wysoką, utrzymywaną przepustowością.
W skrócie: z Node, możesz zepchnąć zapisy do bazy danych na bok i zająć się nimi później, postępując tak, jakby się udało.
STREAMING DANYCH
W bardziej tradycyjnych platformach internetowych, żądania i odpowiedzi HTTP są traktowane jak odizolowane zdarzenia; w rzeczywistości są to strumienie. Ta obserwacja może być wykorzystana w Node.js, aby zbudować kilka fajnych funkcji. Na przykład, możliwe jest przetwarzanie plików w trakcie ich przesyłania, ponieważ dane przychodzą przez strumień i możemy je przetwarzać w trybie online. Można to zrobić dla kodowania audio lub wideo w czasie rzeczywistym i proksowania między różnymi źródłami danych (zobacz następną sekcję).
PROXY
Node.js jest łatwo zatrudniany jako proxy po stronie serwera, gdzie może obsługiwać dużą liczbę jednoczesnych połączeń w sposób nieblokujący. Jest to szczególnie przydatne do proksowania różnych usług o różnych czasach odpowiedzi lub zbierania danych z wielu punktów źródłowych.
Przykład: rozważ aplikację po stronie serwera komunikującą się z zasobami stron trzecich, pobierającą dane z różnych źródeł lub przechowującą zasoby takie jak obrazy i filmy w usługach chmurowych stron trzecich.
Pomimo że istnieją dedykowane serwery proxy, użycie Node zamiast nich może być pomocne, jeśli twoja infrastruktura proksowania nie istnieje lub jeśli potrzebujesz rozwiązania dla lokalnego rozwoju. Rozumiem przez to, że mógłbyś zbudować aplikację po stronie klienta z serwerem deweloperskim Node.js dla aktywów i proxy/stubbing żądań API, podczas gdy w produkcji obsługiwałbyś takie interakcje za pomocą dedykowanej usługi proxy (nginx, HAProxy, itp.).
BROKERAGE – STOCK TRADER’S DASHBOARD
Powróćmy do poziomu aplikacji. Innym przykładem, gdzie dominuje oprogramowanie desktopowe, ale może być łatwo zastąpione rozwiązaniem internetowym w czasie rzeczywistym, jest oprogramowanie handlowe maklerów, używane do śledzenia cen akcji, wykonywania obliczeń/analizy technicznej i tworzenia wykresów.
Przejście na rozwiązanie internetowe w czasie rzeczywistym pozwoliłoby maklerom łatwo zmieniać stacje robocze lub miejsca pracy. Wkrótce moglibyśmy zacząć widywać ich na plaży na Florydzie… lub Ibizie… lub Bali.
APPLICATION MONITORING DASHBOARD
Inny powszechny przypadek użycia, w którym Node-with-web-sockets pasuje idealnie: śledzenie odwiedzających stronę internetową i wizualizacja ich interakcji w czasie rzeczywistym. Możesz zbierać statystyki w czasie rzeczywistym od użytkownika, a nawet przenieść to na następny poziom, wprowadzając ukierunkowane interakcje z odwiedzającymi poprzez otwarcie kanału komunikacyjnego, gdy osiągną określony punkt w lejku – przykład tego można znaleźć z CANDDi.
Wyobraź sobie, jak mógłbyś poprawić swój biznes, gdybyś wiedział, co robią odwiedzający w czasie rzeczywistym – gdybyś mógł wizualizować ich interakcje. Dzięki dwukierunkowym gniazdom Node.js w czasie rzeczywistym, teraz możesz.
SYSTEM MONITORING DASHBOARD
Teraz odwiedźmy infrastrukturalną stronę rzeczy. Wyobraźmy sobie, na przykład, dostawcę SaaS, który chce zaoferować swoim użytkownikom stronę monitorującą usługi (na przykład stronę stanu GitHub). Dzięki pętli zdarzeń Node.js możemy stworzyć potężny, oparty na przeglądarce internetowej pulpit, który sprawdza statusy usług w sposób asynchroniczny i przesyła dane do klientów za pomocą websockets.
Zarówno wewnętrzne (wewnątrz firmy), jak i publiczne statusy usług mogą być raportowane na żywo i w czasie rzeczywistym za pomocą tej technologii. Pchnij ten pomysł nieco dalej i spróbuj wyobrazić sobie Network Operations Center (NOC) monitorujące aplikacje u operatora telekomunikacyjnego, dostawcy chmury/sieci/hostingu lub jakiejś instytucji finansowej, wszystkie uruchomione na otwartym stosie internetowym wspieranym przez Node.js i websockets zamiast Java i/lub Java Applets.
Uwaga: Nie próbuj budować systemów hard real-time w Node.js (tj. systemów wymagających stałych czasów odpowiedzi). Erlang jest prawdopodobnie lepszym wyborem dla tej klasy aplikacji.
SERVER-SIDE WEB APPLICATIONS
Node.js z Express.js może być również używany do tworzenia klasycznych aplikacji internetowych po stronie serwera. Jednakże, chociaż jest to możliwe, ten paradygmat żądanie-odpowiedź, w którym Node.js przenosiłby wyrenderowany HTML, nie jest najbardziej typowym przypadkiem użycia. Istnieją argumenty za i przeciw takiemu podejściu. Oto kilka faktów do rozważenia:
Pros:
- Jeśli twoja aplikacja nie ma żadnych obliczeń intensywnie obciążających procesor, możesz zbudować ją w Javascript od góry do dołu, nawet do poziomu bazy danych, jeśli używasz JSON przechowywania Object DB jak MongoDB. To znacznie ułatwia rozwój (w tym zatrudnianie).
- Crawlery otrzymują w pełni renderowaną odpowiedź HTML, która jest o wiele bardziej przyjazna dla SEO niż, powiedzmy, Single Page Application lub aplikacja websockets uruchomiona na szczycie Node.js.
Konsekwencje:
- Każde obliczenie intensywne dla CPU zablokuje responsywność Node.js, więc platforma wątkowa jest lepszym podejściem. Alternatywnie możesz spróbować skalowania obliczeń(*).
- Używanie Node.js z relacyjną bazą danych jest nadal dość bolesne (zobacz poniżej, aby uzyskać więcej szczegółów). Zrób sobie przysługę i podnieś dowolne inne środowisko, takie jak Rails, Django lub ASP.Net MVC, jeśli próbujesz wykonywać operacje relacyjne.
(*) Alternatywą dla obliczeń intensywnych dla procesora jest stworzenie wysoce skalowalnego środowiska wspieranego przez MQ z przetwarzaniem back-end, aby zachować Node jako front-facing „urzędnik” do obsługi żądań klienta asynchronicznie.
SERVER-SIDE WEB APPLICATION WITH A RELATIONAL DATABASE BEHIND
Porównując Node.js z Express.js przeciwko Ruby on Rails, na przykład, istnieje czysta decyzja na korzyść tego ostatniego, jeśli chodzi o relacyjny dostęp do danych.
Relacyjne narzędzia DB dla Node.js są nadal dość słabo rozwinięte, w porównaniu z konkurencją. Z drugiej strony, Railsy automatycznie zapewniają konfigurację dostępu do danych zaraz po wyjęciu z pudełka, wraz z narzędziami wspierającymi migrację schematów DB i innymi klejnotami (kalambury zamierzone). Railsy i ich rówieśnicze frameworki mają dojrzałe i sprawdzone implementacje warstwy dostępu do danych Active Record lub Data Mapper, których będzie ci bardzo brakowało jeśli spróbujesz je zreplikować w czystym JavaScript.(*)
Jeszcze, jeśli jesteś naprawdę skłonny pozostać JS all-the-way, sprawdź Sequelize i Node ORM2.
(*) Możliwe i nierzadkie jest używanie Node.js wyłącznie jako publicznej fasady, przy zachowaniu back-endu Rails i jego łatwego dostępu do relacyjnego DB.
HEAVY SERVER-SIDE COMPUTATION/PROCESSING
Gdy chodzi o ciężkie obliczenia, Node.js nie jest najlepszą platformą. Nie, zdecydowanie nie chcesz budować serwera obliczeń Fibonacciego w Node.js. Ogólnie rzecz biorąc, każda operacja intensywnie wykonywana przez procesor unieważnia wszystkie korzyści związane z przepustowością, jakie Node oferuje dzięki swojemu sterowanemu zdarzeniami, nieblokującemu modelowi I/O, ponieważ wszelkie przychodzące żądania zostaną zablokowane, podczas gdy wątek jest zajęty przez twoje obliczenia numeryczne.
Jak stwierdzono wcześniej, Node.js jest jednowątkowy i używa tylko jednego rdzenia procesora. Jeśli chodzi o dodanie współbieżności na serwerze wielordzeniowym, jest trochę pracy wykonywanej przez zespół rdzenia Node w postaci modułu klastra. Możesz również uruchomić kilka instancji serwera Node.js dość łatwo za odwrotnym proxy przez nginx.
Z klastrem, nadal powinieneś odciążyć wszystkie ciężkie obliczenia do procesów tła napisanych w bardziej odpowiednim do tego środowisku i mając je komunikujące się przez serwer kolejki komunikatów, taki jak RabbitMQ.
Nawet jeśli twoje przetwarzanie tła może być początkowo uruchomione na tym samym serwerze, takie podejście ma potencjał do bardzo wysokiej skalowalności. Te usługi przetwarzania w tle mogą być łatwo rozprowadzane do oddzielnych serwerów robotniczych bez potrzeby konfigurowania obciążeń serwerów internetowych front-facing.
Oczywiście, używałbyś tego samego podejścia również na innych platformach, ale z Node.js dostajesz wysoką przepustowość, o której mówiliśmy, ponieważ każde żądanie jest małym zadaniem obsługiwanym bardzo szybko i efektywnie.
Wniosek
Przedyskutowaliśmy Node.js od teorii do praktyki, zaczynając od jego celów i ambicji, a kończąc na jego słodkich punktach i pułapkach. Kiedy ludzie napotykają problemy z Node, prawie zawsze sprowadzają się one do tego, że operacje blokujące są korzeniem wszelkiego zła – 99% niewłaściwych zastosowań Node jest tego bezpośrednią konsekwencją.
Pamiętaj: Node.js nigdy nie został stworzony, aby rozwiązać problem skalowania obliczeń. Został stworzony, aby rozwiązać problem skalowania I/O, co robi naprawdę dobrze.
Dodaj komentarz