Tento dokument môže byť v porovnaní s dokumentáciou v angličtine zastaralý. Aktuálne informácie nájdete v dokumentácii v angličtine.

Osvedčené postupy pre Express v produkcii: výkonnosť a spoľahlivosť

Prehľad

Tento článok popisuje niektoré osvedčené postupy z pohľadu výkonnosti a spoľahlivosti Express aplikácií v produkcii.

Táto časť jasne spadá do tzv. “devops” sveta, dotýkajúca sa tradičného vývoja a prevádzky. Podľa toho sú tieto informácie rozdelené do dvoch častí:

Kroky, ktoré je potrebné vykonať vo vašom kóde

Dodržiavanie nasledujúcich postupov vo vašom kóde môže viesť k zlepšeniu výkonnosti vašej aplikácie:

Používajte gzip kompresiu

Použitie gzip kompresie môže veľmi znížiť veľkosť response body a tým zvýšíť rýchlosť webovej aplikácie. Pre zapnutie gzip kompresie vo vašej Express aplikácii používajte compression middleware. Napr.:

const compression = require('compression')
const express = require('express')
const app = express()
app.use(compression())

Pre stránky s vysokou návštevnosťou sa odporúča implementovať kompresiu na úrovni reverse proxy (pozrite sa na Použitie reverse proxy). V takom prípade nemusíte použiť compression middleware. Pre viac detailov ohľadom zapnutia gzip kompresie na Nginx serveri sa pozrite na Module ngx_http_gzip_module v Nginx dokumentácii.

Nepoužívajte synchrónne funkcie

Synchrónne funkcie a metódy “držia” proces vykonania až do kým nebudú spracované. Jedno volanie synchrónnej funkcie môže trvať pár mikrosekúnd, či milisekúnd, avšak v prípade stránok s vysokou návštevnosťou, takéto volania znižujú výkonnosť aplikácie. Preto sa ich používaniu v produkcii vyhnite.

Hoci samotný Node i mnohé jeho moduly poskytujú synchrónne a asynchrónne verzie ich funkcií, v produkcii vždy používajte ich asynchrónne verzie. Jediná situácia, kedy by malo použitie synchrónnej funkcie opodstatnenie, je pri prvotnom spustení aplikácie.

Ak používate Node.js 4.0+ alebo io.js 2.1.0+, môžete použiť prepínač --trace-sync-io, ktorý vypíše warning a stack trace vždy, keď vaša aplikácia použije synchrónne API. V produkcii to samozrejme nepoužívajte, ale už pri developmente sa uistite, že vaša aplikácia je pripravená na produkciu. Pre viac informácií sa pozrite na Weekly update for io.js 2.1.0.

Pre servovanie statických súborov používajte middleware

V developmente môžete pre servovanie statických súborov používať res.sendFile(). V produkcii to však nepoužívajte, pretože táto funkcia musí pri každom requeste čítať dáta z file systému, čo má za následok značné oneskorenie a celkovo nepriaznivo ovlyvňuje výkonnosť aplikácie. res.sendFile() funkcia nie je implementovaná pomocou sendfile systémového volania, ktoré by ju robilo oveľa efektívnejšou.

Namiesto toho používajte serve-static middleware (prípadne podobný ekvivalent), ktorý je optimalizovaný pre servovanie statických súborov v Express aplikáciách.

Ešte lepšou možnosťou pre servovanie statických súborov je použitie reverse proxy; pre viac informácií sa pozrite na Použitie reverse proxy.

Správne logujte

Vo všeobecnosti existujú dva dôvody k logovaniu vo vašej aplkácii a to debugovanie a logovanie aktivít vašej aplikácie. Použitie console.log() príp. console.err() k vypísaniu log správy je bežnou praxou počas developmentu. Avšak pozor, tieto funkcie sú synchrónne v prípade, ak je výstupom terminál príp. súbor, takže nie sú vhodné pre produkčné prostredie, pokiaľ výstup nepresmerujete do iného programu.

Logovanie z dôvodu debugovania

Ak používate na debugovanie logovanie pomocou console.log(), používajte radšej špeciálny modul na debugovanie, ako napr. debug. Tento modul vám umožňuje použivať environment premennú DEBUG, pomocou ktorej dokážete kontrolovať, ktoré debug výpisy budú vypísané pomocou console.err(), príp. žiadne. Ak chcete, aby vaša aplikácia bola čisto asynchrónna, budete stále potrebovať presmerovať výstup console.err() do iného programu. Ale v skutočnosti asi nechcete debugovať v produkcii, však?

Logovanie aktivít aplikácie

Ak používate logovanie na sledovanie aktivít aplikácie (napr. sledovanie traffic-u, príp. API volaní), používajte namiesto console.log() logovacie knižnice, ako sú napr. Winston či Bunyan. Ak vás zaujíma detailnejšie porovnanie týchto dvoch knižníc, prečítajte si tento blog post: Comparing Winston and Bunyan Node.js Logging.

Správne odchytávajte a spracovávajte výnimky

V prípade neodchytenia výnimky Node.js aplikácia spadne. Tzn, že v prípade nespracovania výnimky a nevykonania správnej akcie vaša Express aplikácia spadne. Ak budete pokračovať podľa rád v časti Zabežpečte, aby sa vaša aplikácia automaticky reštartovala, tak sa vaša aplikácia z pádu zotaví. Express aplikácie potrebujú naštastie len krátky čas k naštartovaniu. Bez ohľadu nato, by ste sa mali pádom aplikácie v prvom rade vyhnúť a k tomu potrebujete správne odchytávať výnimky.

K uisteniu sa, že spracovávate všetky výnimky, používajte tieto techniky:

Predtým, ako sa hlbšie pustíme do týchto tém, mali by ste mať základné znalosti Node/Express error handlingu, akými sú používanie error-first callback-ov a šírenie errorov middlewarmi. Node používa pre návrat errorov z asynchrónnych funkcií konvenciu “error-first callbackov”, kde prvým argumentom callback funkcie je error objekt, nasledovaný ostatnými návratovými hodnotami úspešného spracovanie funkcie. Ak nenastal žiaden error zabezpečte, aby prvým parametrom bol null. Definícia callback funkcie musí korešpondovať s error-first callback konvenciou a musí zmysluplne spracovať error. V Express aplikáciách je pre šírenie erroru middlewarmi osvedčenou a odporúčanou technikou použitie next() funkcie.

Pre viac informácií ohľadom základov error handlingu sa pozrite na:

Čo nerobiť

Jedna z vecí, ktorú by ste robiť nemali je počúvať na uncaughtException event, ktorý je emitovaný v okamihu kedy výnimka “bublá” celou cestu späť do event loop-u. Pridanie event listenera uncaughtException zmení defaultné chovanie procesu, ktorý narazil na výnimku; proces bude pokračovať napriek výnimke. Toto sa môže zdať ako dobrým riešením, ako predísť pádu vašej aplikácie, avšak pokračovanie behu vašej aplikácie, v prípade neodchytenej výnimky je nebezpečnou praktikou a nepodporúča sa, pretože sa tým stav procesu stáva nespoľahlivým a nepredpovedateľným.

Navyše, použitie uncaughtException je oficiálne uznané ako hrubé a existuje návrh na jeho odstránenie z jadra. Takže počúvanie na uncaughtException nie je dobrým nápadom. To je dôvod, prečo odporúčame veci ako viacero procesov a supervisorov: pád a reštartovanie je často najspolalivejším spôsobom zotavenia sa z erorru.

Taktiež neodporúčame používať domain modul. Všeobecne nerieši žiaden problém a je označený ako deprecated modul.

Používajte try-catch

Try-catch je klasická konštrukcia v jazyku JavaScript, pomocou ktorej dokážete odchytiť výnimky v synchrónnom kóde. Použite try-catch, napr. na spracovanie chýb pri JSON parsingu, ako na ukážke nižšie.

Používajte nástroje JSHint príp. JSLint, ktoré vám pomôžu nájsť implicitné výnimky, ako napr. reference errors on undefined variables.

Tu je príklad použitia try-catch k odchyteniu potenciálnej výnimky zapríčiňujúcej pád procesu. Táto middleware funkcia príjma query parameter nazvaný “params” ktorý je JSON objekt.

app.get('/search', (req, res) => {
  // Simulating async operation
  setImmediate(() => {
    const jsonStr = req.query.params
    try {
      const jsonObj = JSON.parse(jsonStr)
      res.send('Success')
    } catch (e) {
      res.status(400).send('Invalid JSON string')
    }
  })
})

Pozor, try-catch funguje len pre synchrónny kód. Vzhľadom nato, že Node platforma je primárne asynchrónna (obzvlášť v produkčnom prostredí), veľa výnimiek try-catch neodchytí.

Používajte promises

Promises dokážu spracovať všetky typy výnimiek (explicitné aj implicitné) v asynchrónnych blokoch kódu používajuce then(), pridaním .catch(next) na koniec promise reťazca. Napr.:

app.get('/', (req, res, next) => {
  // do some sync stuff
  queryDb()
    .then((data) => makeCsv(data)) // handle data
    .then((csv) => { /* handle csv */ })
    .catch(next)
})

app.use((err, req, res, next) => {
  // handle error
})

Takto sa všetky asynchrónne i synchrónne errory prešíria do error middleware-u.

Avšak, dve upozornenia:

  1. Všetky vaše asynchrónne kódy musia vracať promises (okrem emitorov). Ak niektorá z knižníc nevracia promises, konvertnite základný objekt použitím funkcie ako napr. Bluebird.promisifyAll().
  2. Event emitory (ako sú streams) môžu spôsobiť neodchytené výnimky. Preto sa uistite, že správne spracovávate error eventy. Napr.:
const wrap = fn => (...args) => fn(...args).catch(args[2])

app.get('/', wrap(async (req, res, next) => {
  const company = await getCompanyById(req.query.id)
  const stream = getLogoStreamById(company.id)
  stream.on('error', next).pipe(res)
}))

Pre viac informácií ohľadom error handling-u použitím promises si prečítajte:

Kroky, ktoré je potrebné vykonať na vašom prostredí

Tu je niekoľko krokov, ktoré môžete vykonať na vašom environment-e pre zlepšenie výkonnosti vašej aplikácie:

Nastavte NODE_ENV premennú na “production”

NODE_ENV environment premenná špecifikuje, v ktorom environmente vaša aplikácia beží, zvyčajne (development alebo production). Jedna z najjednoduchších vecí, ktorú môžete vykonať k zlepšeniu výkonnosti vašej aplikácie je nastavenie premennej NODE_ENV na “production”.

Nastavenie NODE_ENV na “production” zabezpečí, aby Express:

Testy ukazujú, že tym dokážete zlepšiť výkonnosť aplikácie až trojnásobne!

Ak potrebujete písať kód, ktorý je environment-specific, môžete hodnotu NODE_ENV premennej zistiť pomocou process.env.NODE_ENV. Pamätajte však nato, že zisťovanie hodnoty akejkoľvek environment premennej má čiastočný dopad na výkonnosť, preto by ste to mali robiť skôr sporadicky.

Počas vývoja nastavujete environment premenné zvyčajne pomocou shellu, napr. použitím export vo vašom .bash_profile súbore. Toto by ste však nemali robiť na produkčnom serveri; namiesto toho, použite init systém vášho operačného systému (systemd alebo Upstart). Nasledujúca sekcia poskytuje viac detailov ohľadom použitia init systému, pričom nastavenie NODE_ENV premennej je veľmi dôležité z pohľadu výkonnosti (a zároveň veľmi jednoduché), ako je načrtnuté tu:

Pomocou Upstart, použite kľúčové slovo env vo vašom job súbore. Napr.:


# /etc/init/env.conf
 env NODE_ENV=production

Pre viac informácií si prečítajte Upstart Intro, Cookbook and Best Practices.

Pomocou systemd, použite direktívu Environment vo vašom unit súbore. Napr.:


# /etc/systemd/system/myservice.service
Environment=NODE_ENV=production

Pre viac informácií si prečítajte Using Environment Variables In systemd Units.

Ak používate StrongLoop Process Manager, môžete nastaviť environment premennú taktiež keď nainštalujete StrongLoop PM ako službu.

Zabežpečte, aby sa vaša aplikácia automaticky reštartovala

V produkcii zvyčajne nechcete, aby vaša aplikácia bola offline, nikdy. To znamená, že musíte zabezpečiť, aby sa reštartovala v obidvoch prípadoch, či už je to pád aplikácie, alebo servera samotného. Hoci si určite želáte, aby sa ani jedna z týchto vecí nestala, musíte s tým počítať pomocou:

Node aplikácie zhavarujú v prípade výskytu neodchytenej výnimky. Ako prvé by ste sa mali uistiť, že vaša aplikácia je dostatočne otestovaná a spracováva všetky výnimky (pre viac detailov si pozrite časť Správne odchytávajte a spracovávajte výnimky). Ako záchranu vytvorte/nastavte mechanizmus automatického reštartu.

Používajte správcu procesov

Počas vývoja štartujete vašu aplikáciu jednoducho z príkazového riadka pomocou node server.js, príp. niečoho podobného. Tento spôsob je však v prípade produkcie cesta do pekla. Ak vaša aplikácia spadne, bude offline až dokým ju nereštartujete. Aby ste sa uistili, že sa vaša aplikácia v prípade pádu reštartuje, používajte správcu procesov. Správca procesov je “kontainer” pre aplikácie, ktorý vám pomáha pri deploymente, poskytuje vysokú dostupnosť a umožňuje správu aplikácie v runtime.

Správca procesov umožňuje okrem automatického reštartu vašej aplikácie taktiež:

Spomedzi správcov procesov pre Node sú najpopulárnejši:

Pre detailnejšie porovnanie vlastností si pozrite http://strong-pm.io/compare/.

Použitím hociktorého z týchto správcov procesov zabezpečíte, aby vaša aplikácia zostala “hore” i v prípade občasného pádu.

Avšak, StrongLoop PM má veľa ďalších features špeciálne určené pre produkčné prostredie. Môžete ich použiť na:

Ako je vysvetlené nižšie, pri inštalácii StrongLoop PM pomocou init systému, ako služby operačného systému, sa služba automaticky reštartuje po reštartovaní systému. Takto bude vaša aplikácia a cluster bežať navždy.

Používajte init systém

Ďalšiou vrstvou spoľahlivosti je zabezpečenie, aby sa vaša aplikácia reštartovala pri reštartovaní servera. Systémy môžu spadnúť z rôznych dôvodov. Aby bolo zaistené, aby sa vaša aplikácie reštartovala v prípade, ak dôjde k chybe servera, použite init systém zabudovaný do vášho operačného systému. Dva hlavné init systémy používané v súčasnosti sú systemd a Upstart.

Existujú dva spôsoby použitia init systémov s vašou Express aplikáciou:

Systemd

Systemd je správca služieb používaný niektorými distribúciami Linuxu. Väčšina hlavných linuxových distribúcií prijala systemd ako svoj defaultný init systém.

Konfiguračný súbor pre systemd sa nazýva unit file, ktorého názov má príponu .service. Tu je príklad súboru pre priamu správu Node aplikácie (nahradte tučný text s hodnotami vášho systéme a aplikácie):


[Unit]
Description=Awesome Express App

[Service]
Type=simple
ExecStart=/usr/local/bin/node /projects/myapp/index.js
WorkingDirectory=/projects/myapp

User=nobody
Group=nogroup

# Environment variables:
Environment=NODE_ENV=production

# Allow many incoming connections
LimitNOFILE=infinity

# Allow core dumps for debugging
LimitCORE=infinity

StandardInput=null
StandardOutput=syslog
StandardError=syslog
Restart=always

[Install]
WantedBy=multi-user.target

Pre viac informácií ohľadom systemd si prečítajte systemd reference (man page).

StrongLoop PM ako systemd služba

StrongLoop PM možete jednoducho nainštalovať ako systemd službu. Následne, v prípade že nastane reštart servera, systemd automaticky reštartuje i StrongLoop PM, ktorý následne reštartuje i aplikácie ktoré spravuje.

Pre inštaláciu StrongLoop PM ako systemd služby spustite:

$ sudo sl-pm-install --systemd

Potom spustite službu pomocou:

$ sudo /usr/bin/systemctl start strong-pm

Pre viac informácií si prečítajte Setting up a production host (StrongLoop documentation).

Upstart

Upstart je systémový nástroj dostupný v mnohých linuxových distribúciách slúžiaci na spúšťanie taskov a služieb počas štartu systému, ich zastavenie počas vypnutia a dohľadu nad nimi. Vašu Express aplikáciu, alebo správcu procesov môžete nakonfigurovať ako službu a potom Upstart zabezpečí jej reštart v prípade pádu.

Upstart služba je definovaná v konfiguračnom súbore (tiež nazývaný “job”) s názvom súboru končiacim .conf. Nasledujúci príklad ukazuje, ako vytvoriť job súbor s názvom “myapp” pre aplikáciu s názvom “myapp” s hlavným súbor umiestneným v /projects/myapp/index.js.

Vytvorte súbor s názvom myapp.conf umiestnený v /etc/init / s nasledujúcim obsahom (nahraďte tučný text s hodnotami pre váš systém a aplikáciu):


# When to start the process
start on runlevel [2345]

# When to stop the process
stop on runlevel [016]

# Increase file descriptor limit to be able to handle more requests
limit nofile 50000 50000

# Use production mode
env NODE_ENV=production

# Run as www-data
setuid www-data
setgid www-data

# Run from inside the app dir
chdir /projects/myapp

# The process to start
exec /usr/local/bin/node /projects/myapp/index.js

# Restart the process if it is down
respawn

# Limit restart attempt to 10 times within 10 seconds
respawn limit 10 10

Pozn.: Tento skript vyžaduje Upstart 1.4 príp. novší, podporovaný na Ubuntu 12.04-14.10.

Potom, ako je job nakonfigurovaný k spusteniu po štarte systému, bude vaša aplikácia spustená spolu s operačným systémom a automaticky reštartovaná v prípade pádu aplikácie alebo reštartu samotného systému.

Okrem automatického reštartovania aplikácie, Upstart umožňuje použíť tieto príkazy:

Pre viac informácií ohľadom Upstart si prečítajte tu: Upstart Intro, Cookbook and Best Practises.

StrongLoop PM ako Upstart služba

StrongLoop PM možete jednoducho nainštalovať ako Upstart službu. Následne, v prípade že nastane reštart servra, Upstart automaticky reštartuje i StrongLoop PM, ktorý následne reštartuje aj aplikácie ktoré spravuje.

Pre inštaláciu StrongLoop PM ako Upstart 1.4 služby:

$ sudo sl-pm-install

Pre spustenie služby:

$ sudo /sbin/initctl start strong-pm

Pozn.: Pre systémy bez podpory Upstart 1.4 sú príkazy mierne odlišné. Pre viac informácií sa pozrite na Setting up a production host (StrongLoop documentation).

Zabezpečte aby vaša aplikácia bežala v clusteri

V prípade multi-core systémov môžete zvýšiť výkonnosť Node aplikácie niekoľko násobne jej spustením clusterom procesov. V clusteri beží niekoľko inštancií aplikácie, ideálne jedna pre každé CPU jadro, čím sa rozloží záťaž a úlohy medzi jednotlívé inštancie.

Balancovanie medzi inštanciami aplikácie použitím cluster API

Dôležité: Od momentu kedy inštancia aplikácie beží ako samostatný proces, nezdieľajú spoločnu pamäť. Tzn. objekty sú lokálne pre každú inštanciu aplikácie. Preto nedokážete spravovať stav v kóde aplikácie. Namiesto toho môžete k ukladaniu dát týkajúcich sa session a stavu použiť tzv. in-memory datastore ako Redis. Toto varovanie platí v podstate pre všetky formy horizontálneho škálovania, či už clustering s viacerými procesmi alebo viacero fyzických servrov.

V clusterovaných aplikáciách, worker procesy môžu spadnúť individuálne bez toho, aby ovplyvnili zvyšok procesov. Okrem výhody z pohľadu výkonnosti, izolovanie chybovosti je ďalším dôvodom pre beh clustra procesov aplikácie. V prípade, že worker proces spadne zabezpečte, že vždy sa zalogujte event a vytvorí nový process (spawn) pomocou cluster.fork().

Použite Node cluster modulu

Clustering je možný pomocou Node cluster modulu. Ten dovoľuje master procesu vyrobiť (spawn) worker procesy a rozdistribuovať prichádzajúce spojenia medzi workerov. Avšak, lepšie než priame použitie tohto modulu je použiť jeden z mnohých existujúcich toolov, ktoré to robia automaticky, napr. node-pm alebo cluster-service.

Použitie StrongLoop PM

V prípade, že deploynete vašu aplikáciu do StrongLoop Process Manager (PM), získate tým výhody clusteringu bez modifikácie kódu vašej aplikácie.

Keď StrongLoop Process Manager (PM) spúšta aplikáciu, aplikácia je spustená automaticky v clusteri s takým množstvom workerov, aý je počet jadier CPU systéme. Počet worker procesov v clustri môžete manuálne zmeniť použitím slc nástroja bez nutnosti stopnutia aplikácie.

Napr., predpokladajúc, že ste deployli vašu aplikáciu na prod.foo.com a StrongLoop PM počúva na porte 8701 (defaultný), tak nastavenie veľkosti clustera na osem vykonáte pomocou slc takto:

$ slc ctl -C http://prod.foo.com:8701 set-size my-app 8

Pre viac informácií ohľadom clusteringu pomocou StrongLoop PM sa pozrite na časť Clustering v StrongLoop dokumentácii.

Cache-ovanie odpovedí requestov

Ďalšou stratégiou pre zlepšenie výkonosti v produkcii je cachovanie odpovedí na prichádzajúce requesty, čím zabezpečíte, že vaša aplikácia nemusí opakovane vykonávat tie isté operácie pre obslúženie rovnakých requestov.

Použitie caching servra, ako [Varnish] (https://www.varnish-cache.org/) alebo Nginx (pozrite si tiež Nginx Caching) výrazne zvýši rýchlosť a výkon vášej aplikácie.

Použitie load balancera

Bez ohľadu na to, ako je optimalizovaná aplikácia, jedna inštancia môže spracovať iba obmedzené množstvo záťaže a requestov. Jedným spôsobom škálovania aplikácie je spustenei jej viacerých inštancií a distribuovať zaťaženie pomocou load balancera. Zapojenie load balancera môže zlepšiť výkon a rýchlosť vašej aplikácie a umožní jej vačšie škálovanie, než by bolo možné v prípade jedinej inštancie.

Load balancer je zvyčajne reverzné proxy, ktoré organizuje prevádzku medzi viacerými inštanciami aplikácie a serverov. Load balancer môžete pre vašu aplikáciu setupnúť jednoducho použítím Nginx, alebo HAProxy.

Load balancer zabezpečí správne spárovanie requestov súvisiacich s konkrétnym session ID a procesom, ktorý túto session spravuje. Tento prístup sa nazýva session affinity, alebo sticky sessions a môže byť riešený návrhom popísaným vyššie, teda použitím dátového úložiska ako je Redis (v závislosti od aplikácie). Prečítajte si nasledujúcu diskusiu Using multiple nodes.

Použitie StrongLoop PM spolu s Nginx load balancerom

StrongLoop Process Manager je integrovaný s Nginx Controller-om, čo uľahčuje konfiguráciu multi-host produkčného prostredia. Pre viac informácií sa pozrite na Scaling to multiple servers (StrongLoop dokumentácia).

Použitie reverzného proxy

Reverzné proxy je umiestnené pred webovou aplikáciou a vykonáva podporné operácie k requestom smerovaných na aplikáciu. Medzi inným, dokáže handlovať stránky s errormi, kompresiu, caching, servovanie súborov a load balancing.

Odovzdanie úloh, ktoré nevyžadujú znalosť stavu aplikácie reverznému proxy, odbremení Express aplikáciu a umožní jej vykonávať špecializované úlohy. Z tohto dôvodu sa v produkcii odporúča fungovanie Express za reverzným proxy ako je Nginx či HAProxy.