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
require('total.js')
sa bude volať require('total4')
NPM inštalácia, ktorú budete milovať:
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()
.
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.
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:
API
endpointAk 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:
route
, ktorá spracúva všetky endpoint dataAPI
obsahuje názvy akcií, ktoré sa majú spustiť namiesto komplikovanej URL adresyUkážka šifrovania:
Ukážka routingu:
Ukážka na client-side za pomoci jComponent knižnice:
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 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.
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 encryptovanieschema.compress()
odstráni všetky null
a false
hodnoty z dát pri serializácii do JSON
model
do všetkých operácií s výnimkou setQuery()
a setRead()
, príklad: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ť:
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))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ť:
UID
súčasne (ani v clusteri)UID
sa dá deserializovať (obsahuje dátum, počítadlo v danej minúte, checksum, verziu a token)VARCHAR(20)
na primárne kľúčeString.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á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:
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:
Zároveň unix socket podporuje aj RESTBuilder
, REQUEST()
a WEBSOCKETCLIENT()
metóda.
auto
scale módAktualizoval 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 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:
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:
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).
EMIT2()
metódu.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?
JSON
(default), text
a binary
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:
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:
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:
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.
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:
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?
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!