Peter Širka
Peter Širka

Osobný blog programátora a IT nadšenca.

Je tu Total.js 4 - jeden z najlepších Node.js frameworkov

Je tu Total.js 4 - jeden z najlepších Node.js frameworkov

Po vyše 5 mesiacoch tvrdej práce som sa rozhodol vypublikovať novú verziu Node.js frameworku - Total.js 4, ktorá hlavne zjednodušuje vývoj. Ak by som mal zhrnúť 9 rokov vývoja frameworku, tak 9 rokov mi trvalo prísť na to, že veci sa dajú robiť aj jednoduchšie (samozrejme s výnimkami).

Kvôli spätnej kompatibilite som vytvoril nový NPM repozitár s názvom total4, takže inštalácia Total.js 4 je nasledovná:

  • npm install total4
  • miesto require('total.js') sa bude volať require('total4')

NPM inštalácia, ktorú budete milovať:

Total.js 4 installation

Čo je nové?

Z novej verzie Total.js frameworku som odstránil kopec starých vecí, ktoré som vôbec v projektoch nepoužíval a niektoré metódy som premenoval na "lepšie" názvy, napríklad: String.removeDiacritics() bolo premenované na String.toASCII().

Dokumentácia

Napísal som aj novú dokumentáciu (možno to vydám ako open-source, ešte uvidím), ktorá obsahuje úplne všetko z Total.js Platformy. Považujem to za kľúčový moment do budúcnosti Total.js platformy. Trvalo týždne kým som to dal dokopy celé, veľmi mi pomohli naše baby z firmy (Lenka a Lucka - ďakujem), ktoré prekopírovali staré texty do novej dokumentácie a ja som to už len upravoval po nociach.

Dokumentácia

Routing

Asi najväčšou zmenou prešiel routing, celý som ho zjednotil do jednej metódy. Takže naraz je možné deklarovať endpoint na klasický Controller, na WebSocket alebo na File. Kompletne som prepísal aj mechanizmus, ktorý nasmeruje request na správnu trasu - toto považujem za veľkú vec, pretože spracovanie requestu je rýchlejšie zhruba o 10%.

Vylepšil som aj routing, ktorý smeruje priamo do schém:

Nový API endpoint

Ak tvoríte RESTové služby, tak API endpoint budete milovať, pretože nepotrebujete pri ňom rozmýšľať a ponúka veľmi efektívne šifrovanie dát, medzi server <-> client side (a opačne). Implementácia nie je vôbec komplikované, ale šifrovací algoritmus už áno - takže ak by ste chceli použiť nový API endpoint napr. vo Vue alebo v Angular tak to nebude vôbec jednoduché (určite viem s tým pomôcť). Čo sa týka ešte šifrovania, tak šifrované je všetko: data, akcia a query argumenty tiež (všetko je serializované v payload) - vývojár nemusí nič riešiť, on pracuje na server-side a client-side tak, ako doteraz. Šifrovací kľúč musí byť rovnaký na client-side a server-side, čo v podstate robí šifrovanie menej účinným, ale stále je X spôsobov ako kľúč v JavaScripte schovať/nastaviť. Účelom šifrovania je len sťažiť nejaký útok alebo zber dát od potencionálneho útočníka, nič viac.

Výhody API endpointu:

  • rýchlosť, framework vytvára na pozadí iba jednu route, ktorá spracúva všetky endpoint data
  • prehľadnosť, API obsahuje názvy akcií, ktoré sa majú spustiť namiesto komplikovanej URL adresy
  • šifrovanie dát medzi server-side a client-side
  • data sú stále posielané v textovej podobe (chcel som použiť binárnu, ale bál som sa proxy serverov)

Ukážka šifrovania:

API encryption

Ukážka routingu:

Ukážka na client-side za pomoci jComponent knižnice:

Upload súborov

Kompletne som prepísal algoritmus na spracovanie requestov s content-type multipart/form-data. Doteraz Total.js v3 používal interne známy a starý algoritmus formidable a trošku ma to hnevalo za tie roky, lebo som vedel, že to viem napísať aj lepšie - len som ešte na to nemal potrebné vedomosti. Nový mechanismu je rýchlejší zhruba o 15% (a úspornejší na CPU a RAM) oproti starému, ktorý bol implementovaný v Total.js. Z pohľadu vývojára sa nič nemení, ale vo frameworku je to skutočne veľká zmena. Parser som dokonca oddelil a je ho možné použiť aj napríklad na spracovanie emailových správ, ale o tom možno niekedy v budúcnosti.

Nové spracovanie URL query argumentov

Nové spracovanie URL query argumentov je už implementované aj v Total.js v3, avšak je potrebné to znovu zopakovať. Total.js nepoužíva vstavaný URL query parser z Node.js, pretože niektoré veci sa nedali ovplyvniť.

Asi 6 hodín som optimalizoval parser tak, aby bol rovnako výkonný ako URL parser v Node.js. Nechytal som sa rýchlosťou a môj deserializér zaostával zhruba o 20% pozadu. Nevzdal som sa a step-by-step optimalizáciou sa mi ho podarilo dostať na úroveň rýchlosti ako ho má Node.js, ba niekedy o trochu som výkonnostne na tom lepšie.

Podstatné je, že viem regulovať napr. maximálny počet kľúčov (to dokáže aj Node.js parser), ale viem definovať aj maximálnu dĺžku hodnoty kľúča (toto už Node.js parser nedokázal). Zároveň som zakázal parsovanie rovnakých kľúčov do Array príklad: tags=Hodnota1&tags=Hodnota2&tags=Hodnota3 (toto sa nedá vôbec zakázať v Node.js parseri). Riešenie v Total.js je také, že ak si chcete poslať pole z client-side, tak doporočujem posielať hodnoty v tvare: tags=Hodnota1,Hodnota2,Hodnota3 a deserializovať do Array manuálne.

Vylepšené Total.js schemas

Schémy dostali zjednodušenia a vylepšenia tiež. model v schémach už neobsahuje žiadne skryté metódy, obsahuje už priamo spracované údaje a zároveň spracovanie dát cez schémy by malo byť rýchlejšie a oveľa efektívnejšie.

Odstránené:

  • transformations
  • hooks
  • operations

Pridané:

  • operations sú prepojené priamo s NEWOPERATION()
  • tasks sú prepojené priamo s NEWTASK()
  • schema.encrypt() zapína encryptovanie
  • schema.compress() odstráni všetky null a false hodnoty z dát pri serializácii do JSON
  • nový argument model do všetkých operácií s výnimkou setQuery() a setRead(), príklad:

TextDB

TextDB je veľmi veľká vec v Total.js frameworku, je to NoSQL embedded databáza, ktorú som pomenoval ako TextDB. Kompletne som prepísal celý engine na zápis a čítanie údajov. TextDB je čisto textová databáza, takže všetky údaje sú serializované v JSON formáte line-by-line v textovom súbore (data je možné manuálne meniť priamo v súbore). Nový databázový engine je oveľa efektívnejší a mal by byť aj rýchlejší. Pri webovej aplikácií Total.js spúšťa samostatný thread na databázu, takže čítanie/zápis nazaťažuje hlavný thread, kde sa spracúvajú všetky requesty. Od databázy som oddelil počítadlo COUNTER() a aj blob storage, ktorý je súčasťou už tzv. FILESTORAGE(). Málo kto vie, že TextDB (historicky NoSQL embedded databáza) streamuje databázový súbor pri čítaní (na čítanie sa používajú max. 2 workre s tým, že worker môže mať v sebe aj 20 dotazov naraz), rovnako aj pri zápise (avšak, tam je jeden worker) - takže TextDB vie veľmi ľahko a efektívne prečítať/modifikovať aj 100 MB textové databázy.

Databáza je určená iba na malé projekty do 100 000 jednoduchších dokumentov (per databáza). Samozrejme databáz môžete mať viacej a konečne som dopracoval aj reálny INNER JOIN medzi databázami (doteraz fungoval len LEFT JOIN), takže splnil som si môj malý sen. Využitie TextDB je veľmi veľké, používajú ju skoro všetky Total.js open-source projekty ako napríklad: Total.js CMS, Total.js Code Editor, Total.js SuperAdmin, atď..

Poslednou novinkou v TextDB je in-memory mód, ktorý je potrebné nastaviť v config súbore.

Rýchle a efektívne použitie všade:

Dobré vedieť:

  • databáza nemôže byť použitá v clustri ani v Total.js threads
  • pri webových aplikáciách sa pre databázu otvára samostatný thread
  • TextDB Data-Reader je možné použiť pri streamovaní a filtrovaní rôznych dát napr. v IoT (niečo ako Complex Event Processing (CEP))

Tvorba unikátnych identifikátorov

UID() metóda patrí medzi najdôležitejšie metódy v Total.js frameworku, pretože sa používa na tvorbu unikátnych identifikátorov na ukladanie dát alebo súborov. Total.js v4 prináša novú verziu (už 4 verziu) na generovanie:

Dobré vedieť:

  • nová verzia je kratšia o 2 znaky oproti staršej
  • nemôže sa stať, že sa vygenerujú rovnaké UID súčasne (ani v clusteri)
  • každé UID sa dá deserializovať (obsahuje dátum, počítadlo v danej minúte, checksum, verziu a token)
  • do databázy doporučujeme používať VARCHAR(20) na primárne kľúče
  • Total.js 4 a Total.js 3 vie spracovať všetky verzie (staré aj nové) identifikátorov
  • nedá sa na priamo incrementovať ako číslo (čo má svoje výhody a aj nevýhody)
  • dá sa navyše ľahko encryptovať cez String.encrypt_uid()
  • UID generuje variabilný identifikátor, takže ak sa za minútu vyvolá viac ako 1000 krát, tak aj dĺžka bude iná
  • doporučujem používať všade

FileStorage na ukladanie súborov

Trvalo to, kým som prišiel na to ako ukladať efektívne uploadnuté (užívateľské) súbory na servery. Doteraz všetko fungovalo super a prehľadne, ale nefungovalo to v clusteri, takže musel som prehodnotiť celý algoritmus ukladania súborov a výsledok je tu. Ešte dodám, že k súborom sa navyše pridáva hlavička s meta informáciami (2 kB na začiatku súboru) o súbore, takže súbor nie je čiateľný klasickým spôsobom. Meta informácie obsahujú názov súboru, veľkosť, typ, dátum pridania a pokiaľ sa jedná o obrázok, tak obsahujú aj width/height obrázka (btw: do meta informácií je možné pridať ešte menšie custom data). Zlá správa je to, že nový FileStorage nie je vôbec kompatibilný so starým enginom.

Trocha histórie:

Historicky sa súbory ukladali tak, že sa používalo globálne počítadlo súborov, ktoré rozdeľovalo súbory po 1000 súborov per adresár. Štruktúra bola krásna, príklad:

Lenže ak bežala aplikácia v clusteri, tak bolo veľmi ťažké udržať počítadlo pre všetky thready rovnaké. Preto som prišiel s myšlienkou, že miesto nejakého globálneho počítadla sa použijé nejaký identifikátor súboru - v našom prípade rátam s UID() generátorom (môže byť aj iné ID), podľa ktorého sa algoritmom trasformuje ID do názvu adresára v hodnotách od 0000 po 9999 a následne sa do tohto adresára uloží súbor. Takže maximálne nám vznikne 9999 adresárov, ktoré môžu obsahovať tisíce súborov.

Súčasnosť:

Takto to reálne vypadá na disku:

FileStorage structure

Podpora pre Unix socket

Konečne som pridal podporu unix socketu do Total.js frameworku. Od teraz je možné webový server spustiť priamo za pomoci unix socket (nebude fungovať vo Windowsoch), výhody:

  • zvýši sa performance komunikácie medzi Total.js a reverznou proxy
  • bezpečnosť
  • zníži sa zaťaženie siete

Zároveň unix socket podporuje aj RESTBuilder, REQUEST() a WEBSOCKETCLIENT() metóda.

Nový Total.js SuperAdmin podporuje pre Total.js 4 spustenie aplikácie za pomoci unix socketu. V prípade, že Total.js SuperAdmin nepoužívate, doporučujem pozrieť predpripravený starting script.

Total.js cluster - auto scale mód

Aktualizoval som aj Total.js Cluster, pridal som tam veľmi zaujímavú funkciu - auto scale mód, ktorý dokáže automaticky pridať nové thready, keď aplikácia nestíha reagovať a zároveň vie odstrániť nepotrebné thready.

Takže by default aplikácia začína s jedným threadom a v prípade, že aplikácia nereaguje pod 70 ms, tak sa otvorí nový thread. Samozrejme, je možné limitovať maximálny počet otvorených threadov alebo nechať ich bez obmedzenia.

Vylepšil som aj komunikáciu medzi threadami a to tak, že je možné vyvolať event v rámci každého threadu cez metódu EMIT2(event_name, [arg1], [argN]).

Cluster je možné spustiť jednoducho, stačí použiť starting script, ktorý som pripravil:

Total.js Threads

Total.js Threads vznikli za účelom vytvorenia vysoko výkonnej aplikácie v Total.js frameworku bez dodatočných závislostí. Je to moja osobná vízia server-less architektúry pre Total.js framework.

V podstatne sa jedná o klasickú aplikáciu s tým rozdielom, že do aplikácie pridávate tzv. threads. Každý thread je samostatná aplikácia a má samostatný wildcardový endpoint, kde sú následne presmerované všetky requesty z hlavného serveru. Hlavný thread / inštancia slúži ako reverzná proxy, ktorá vytvorí clusterizované (s auto scale módom) pod-processy (pre každý thread) a následne s nimi komunikuje cez unix socket (takže Total.js Threads nebudú fungovať vo Windowse).

Ukážka štruktúry projektu:

Štruktúra Total.js Threads

Na obrázku je zobrazená štruktúra projektu, kde aplikácia je tvorená z threadov. Po spustení aplikácie spustí main thread každý jeden thread v cluster móde. Každý thread načúva na svojom endpointe, endpoint = názov adresára (je možné pridať prefix v starting script). Každý thread dedí definície, nastavenie, atď. z main threadu. Tu sa dostávame k tomu, prečo to volám server-less architektúra - totiž vývojár programuje už daný thread, jeho funkčnosť a nezaujíma sa o jadro aplikácie, ktoré mu extenduje nejakú predpripravenú funkčnosť. Takže keď sa extenduje aplikácia, tak sa pridá iba nový thread a ten sa naprogramuje (nič viac).

Čo urobí Total.js na pozadí? To popisuje obrázok nižšie:

Total.js Threads

Výkon je brutálny, bavíme sa o tom, že takýto typ aplikácie vie spracovať desatisíce requestov za sekundu. Avšak práve najväčším problémom - úzkym hrdlom (Bottleneck) môže byť práve databáza, ktorá musí byť na takýto typ aplikácií brutálne pripravená. Vo firme sme robili interné testy a nepodarilo sa nám dostatočne zaťažiť aplikáciu. Za 2 minúty malý testovací server odoslal viac ako 12 GB dát, mal otvorených 6 threadov a hlavný thread žral iba 70 MB-RAM a pod-procesy si zobrali okolo 150 MB pre každý thread + mali sme tam implementovaný aj menší sleep time pre process, aby sme videli či sa správne otvárajú a zatvárajú thready v cluster móde (všetko fungovalo ako po masle).

Threads podporujú aj komunikáciu medzi všetkými otvorenými threadami rovnako ako v čistom cluster mode cez EMIT2() metódu.

Vylepšený WebSocket

V staršej verzii Total.js bola v implementácii WebSocket malá chyba, ktorá spôsobovala to, že Safari nevedelo spracovať UTF-8 znaky (preto sa tam používal URI encode/decode hodnôt). Toto som samozrejme opravil v novej verzii Total.js frameworku + pridal som podporu pre frame masking, toto chýbalo. Z WebSocketu som ešte vymenil ArrayBuffer za Buffer, takže ak sa jedná o binárnu komunikáciu, tak je to TERAZ odosť jednoduchšie ako to bolo predtým.

Málo kto vedel, že v Total.js je aj metóda WEBSOCKETCLIENT(), ktorá sa dokáže napojiť na WebSocket priamo zo servera. Túto metódu je možné použiť na komunikáciu aj medzi Total.js aplikáciami napr. pri zdieľanej session alebo v iných prípadoch.

Takže čo podporuje Total.js WebSocket?

  • komprimáciu správ cez GZIP
  • frame masking
  • podporuje enkódovanie v JSON (default), text a binary
  • reverezný chod, vieme sa napojiť z Total.js na 3rd party WebSocket server

Novinka Builds

Toto je úplne nová vec v Total.js frameworku a slúži pre náš online tool zvaný Total.js AppBuilder - UIčko na tvorbu Total.js appiek. O tejto novinky budem písať v samostatnom blogu v blízkej budúcnosti. Malá ochutnávka:

Total.js App Builder

Zmenený a vylepšený FlowStream

FlowStream kvázi obsahuje iba jadro Total.js Flow. Úplne som upravil a vylepšil + pridal som podporu pre HTML komponenty (viď ukážku kódu nižšie). Na čo je dobrý FlowStream? Na vytvorenie custom riešení ako je Total.js Flow. FlowStream viem na rozdiel od Total.js Flow pripraviť do clustera alebo implementovať lepšie užívateľské prostredie s rôznymi prístupovými právami. K FlowStream som urobil aj UI komponentu j-Flow.

Ukážka FlowStream komponenty:

Nový unit-testing algoritmus

Rozmýšľaľ som nad vytvorením lepšieho mechanizmu na testovanie REST endpointov v Total.js. Doteraz boli potrebné ďalšie nástroje na otestovanie funkčnosti, s čím som nebol stotožnený. Preto som pridal rozšíril controller v Total.js o novú metódu controller.runtest(), ktorá simuluje vytvorenie requestu a tým otestuje funkčnosť endpointu.

Output:

Test output

Total.js Apps Monitoring

Framework od verzie 3 ukladá v intervale 1 minúta - interné štatistiky z frameworku do súboru index.js.json (teda názov súboru je názov štartovacieho skriptu s pridanou .json koncovkou). Total.js 4 tieto štatistiky vylepšuje a pridáva ešte nejaké naviac. A dostávam sa k podstate veci, že služba Total.js Apps Monitor (BEZPLATNÁ) vie monitorovať tieto štatistiky a vypisovať ich v celku do pekného prehľadu (viď obrázok nižšie). Plus pridáva ešte aj to, že keď aplikácia obsahuje chyby (vieš tam poslednú chybu prečítať) alebo je offline, tak ťa automaticky notifikuje.

AppMonitor

Learning curve

Osobne si myslím, že krivku učenia sa mi podarilo znížiť z 3 mesiacov na mesiac pre skúsenejších vývojárov. Celé účenie Total.js frameworku je len v routingu a v schémach. Takže, ak by sa chcel niekto naučiť Total.js framework 4, tak stačí zistiť odpovede na tieto 3 otázky:

  • Ako funguje Total.js Routing?
  • Ako sa spracúvajú data v Total.js schémách?
  • Ako prepojím Total.js Schémy s Total.js routingom?
  • Ako uložím a načítam data v databáze?

Aké Total.js produkty sú už prispôsobené Total.js 4?

Momentálne som prispôsobil skoro všetky Total.js open-source produkty. Strávil som na tom stovky hodín a každý jeden produkt je fakt brutálny. Na vývoj vo firme už nič iné ako Total.js Code nepoužívame, takže väčšina "aktualizácií" Total.js produktov bola vykonaná práve v ňom. Priznám sa, že keď zapínam Sublime, tak som trochu nervózny, pretože Total.js Code som za 2 roky posunul extrémne ďaleko.

Čo všetko som aktualizoval?

Na čo to všetko?

Až po niekoľkých rokoch sa ukazuje, že vytvoriť vlastný server-side framework a client-side UI knižnicu stálo skutočne za to. Bolelo to, bol to pot, krv a mnoho-krát vyčerpanosť. Avšak tie možnosti, ktoré mám teraz a tie produkty, ktoré sa urobili sú znakom, že keď človek chce, všetko sa dá a to som vyštudovaný predavač. Tak dokódenia!

Čo ďalej?