OpenResty: premeníme NGINX na plnohodnotný aplikačný server

OpenResty: premeníme NGINX na plnohodnotný aplikačný serverOpäť zverejňujeme prepis prezentácie z konferencie HighLoad++ 2016, ktorá sa konala v podmoskovskom Skolkovu 7.—8. novembra minulého roku. Vladimir Protasov hovorí, ako rozšíriť funkčnosť NGINX pomocou OpenResty a Lua.

Ahojte, volám sa Vladimir Protasov, pracujem v Parallels. Povedal by som niečo o sebe. Tri štvrtiny svojho života sa zaoberám písaním kódu. Stal som sa programátorom do morku kostí v pravom slova zmysle: niekedy v noci vidím kód aj vo sne. Štvrtinu života – priemyslovou výrobou, písaním kódu, ktorý ide priamo do produkcie. Kód, ktorý niektorí z vás používajú, ale ani netušia, že ho používajú.

Aby ste chápali, ako zlé to bolo. Keď som bol malý junior, prišiel som a dostal som také dvojterabajtové databázy. Dnes má každý highload. Chodieval som na konferencie, pýtal som sa: „Chalani, povedzte, máte big data, všetko super? Koľko máte tam databáz?“ Odpovedali mi: „Máme 100 gigabajtov!“ Hovoril som: „Super, 100 gigabajtov!“ A vnútri som si myslel, ako si udržať poker face. Myslíš, že áno, chalani sú super, a potom sa vrátiš a dupoš s tými mnohoterabajtovými databázami. A to ako junior. Predstavte si, aký to bol úder?

Poznám viac ako 20 programovacích jazykov. To je to, čo som sa musel naučiť počas práce. Dajú ti kód na Erlangu, C, C++, Lua, Python, Ruby a na niečom ďalšom, a ty to všetko musíš vyrobiť. V skratke, musel som. Presný počet sa mi nepodarilo spočítať, ale asi som stratil prehľad pri 20.

Keďže všetci prítomní vedia, čo je Parallels a čím sa zaoberáme, nebudem sa rozprávať o tom, akí sme úžasní a čo robíme. Poviem len, že máme 13 kancelárií po celom svete, viac ako 300 zamestnancov, vývoj v Moskve, Tallinne a na Malte. Ak máte záujem, môžete sa presťahovať na Maltu, ak je zima a potrebujete si trochu zahriať chrbticu.

Konkrétne náš tím pracuje s Python 2. Zaoberáme sa biznisom a nemáme čas zavádzať módne technológie, takže trpíme. Máme Django, pretože obsahuje všetko, čo potrebujeme, a nepotrebné sme vyhodili. Takisto MySQL, Redis a NGINX. Máme veľa ďalších skvelých vecí. Máme MongoDB, máme králiky behajúce, máme čokoľvek – ale to nie je moja práca a tým sa nezaoberám.

OpenResty

O sebe som povedal. Poďme sa pozrieť, o čom budem dnes hovoriť:

  • Čo je OpenResty a na čo je dobrý?
  • Prečo vynášať ďalší bicykel, keď máme Python, NodeJS, PHP, Go a ďalšie skvelé veci, s ktorými sú všetci spokojní?
  • A kúsok príkladov z praxe. Musel som výrazne skrátiť prednášku, pretože vychádzala na 3,5 hodiny, takže príkladov bude málo.

OpenResty je NGINX. Vďaka nemu máme plnohodnotný webový server, ktorý je dobre napísaný a rýchlo funguje. Myslím, že väčšina z nás používa NGINX v produkcii. Všetci viete, že je rýchly a skvelý. Má skvelé synchronné vstupy/výstupy, takže nemusíme nič vymýšľať, ako to robíme v Pythone s gevent. Gevent je skvelý, fajn, ale ak napíšete kód v C a niečo sa pokazí, tak sa z toho s geventem zbláznite pri debagovaní. Mám s tým skúsenosť: trvalo mi celé dva dni, kým som zistil, čo sa skutočne stalo. Keby sa niekto nepokúsil niekoľko týždňov predtým, nenašiel problém, nenapísal to na Internet a Google by to nenašiel, boli by sme úplne zúfalí.

V NGINX už existuje cache a statický obsah. Nemusíte sa obávať, ako to správne urobiť, aby niekde nezačalo spomaľovať, aby ste niekde nestratili deskriptory. Nginx je veľmi pohodlné nasadzovať, nemusíte premýšľať, čo vziať – WSGI, PHP-FPM, Gunicorn, Unicorn. Nginx nainštalujete, odovzdáte administrátorom, oni vedia, ako s tým pracovať. Nginx štruktúrovane spracováva požiadavky. O tom poviem neskôr. Stručne povedané, má fázu, keď len prijal požiadavku, keď ju spracoval a keď odovzdal obsah používateľovi.

Nginx je skvelý, ale je tu jeden problém: nie je dostatočne flexibilný, aj pri všetkých tých skvelých funkciách, ktoré chalani vtlačili do konfigurácie, aj keď je možné ho nastaviť. Tieto možnosti sú nedostatočné. Preto chalani z Taobao pred ôsmimi rokmi, myslím, zakomponovali Lua. Čo to prináša?

  • Veľkosť. Je malý. LuaJIT prináša zhruba 100-200 kilobajtov pamäťového overheadu a minimálny overhead pri výkonnosti.
  • Rýchlosť. Interpreter LuaJIT je v mnohých situáciách blízko C, v niektorých situáciách prehráva proti Jave, v niektorých ju predbehne. Nejaký čas bol považovaný za štandard, najlepší JIT kompilátor. Teraz existujú lepšie, ale sú veľmi ťažké, napríklad ten istý V8. Niektoré JS interpretery a Java HotSpot sú v niektorých bodoch rýchlejšie, ale v niektorých oblastiach stále zaostávajú.
  • Jednoduchosť učenia. Ak máte napríklad kódovú základňu v Perle a nie ste Booking, nenájdete programátorov v Perle. Pretože nie sú, všetkých vzali, a učiť ich trvá dlho a je to náročné. Ak chcete programátorov na niečom inom, možno ich tiež bude treba prekvalifikovať alebo nájsť. V prípade Lua je to jednoduché. Lua sa naučí akýkoľvek junior za tri dni. Mne trvalo asi dve hodiny, aby som sa zorientoval. Po dvoch hodinách som už písal kód do produkcie. Asi po týždni ho priamo poslali do produkcie.

Výsledok vyzerá takto:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Je tu množstvo vecí. V OpenResty skombinovali množstvo modulov, ako lua-ových, tak aj engine-ových. A máte všetko pripravené – nasadili ste a funguje to.

Príklady

Dosť s lyrikou, poďme kódu. Tu je jednoduché Hello World:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Čo je tu? Toto je engine-ový location. Nezaoberáme sa tým, nepíšeme svoj routing, neberieme nejaký hotový – už ho máme v NGINX, žijeme pohodlne a lenivo.

content_by_lua_block – to je blok, ktorý hovorí, že predávame obsah pomocou Lua skriptu. Berieme engine-ovú premennú remote_addr a podávame ju do string.format. To isté, ako sprintf, len na Lua, len správne. A vrátime klientovi.

Výsledok bude vyzerať takto:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Ale vráťme sa do reálneho sveta. Do produkcie nikto nenasadzuje Hello World. Naša aplikácia zvyčajne komunikuje s databázou alebo niekde inde a väčšinu času čaká na odpoveď.

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Prosto sedí a čaká. To nie je veľmi dobré. Keď príde 100 000 používateľov, je nám veľmi ťažko. Preto si vezmime ako príklad jednoduchú aplikáciu. Budeme hľadať obrázky, povedzme, mačiatok. Len nebudeme len tak hľadať, budeme rozširovať kľúčové slová a ak používateľ hľadá „mačiatka“, nájdeme mu mačky, chlpaté a podobne. Na začiatok potrebujeme získať údaje požiadavky na backend. Vyzerá to takto:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Dve riadky vám umožnia získať GET parametre, žiadne zložitosti. Potom, povedzme, z databázy s tabuľkou podľa kľúčového slova a prípony získame túto informáciu bežným SQL príkazom. Všetko je jednoduché. Vyzerá to takto:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Pripojíme knižnicu resty.mysql, ktorá už je u nás v súprave. Nemusíme nič inštalovať, všetko je pripravené. Uvádzame, ako sa pripojiť, a robíme SQL príkaz:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Tu je to trošku strašidelné, ale všetko funguje. Tu je 10 – to je limit. Vyťahujeme 10 záznamov, sme leniví, nechceme ukazovať viac. Na limit v SQL som zabudol.

Ďalej hľadáme obrázky podľa všetkých požiadaviek. Zbierame balík požiadaviek a vypĺňame Lua tabuľku, ktorá sa volá reqs, a robíme ngx.location.capture_multi.

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Všetky tieto požiadavky idú paralelne a vracajú nám odpovede. Čas spracovania je rovný času odpovede najpomalšieho. Ak sa nám všetky spracujú za 50 milisekúnd a poslali sme sto požiadaviek, odpoveď dostaneme za 50 milisekúnd.

Keďže sme leniví a nechceme písať spracovanie HTTP a cacheovania, prinútime NGINX robiť všetko za nás. Ako ste videli, tam bola požiadavka na url/fetch, to je ono:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Robíme jednoduchý proxy_pass, určujeme, kam to zakonzervovať, ako to spraviť, a máme všetko funkčné.

Ale to nestačí, musíme ešte odovzdať dáta používateľovi. Najjednoduchšia myšlienka – všetko serializovať do JSON-u, jednoduché, na dve čiarky. Odovzdávame Content-Type, odovzdávame JSON.

Ale je tu jedna komplikácia: používateľ nechce čítať JSON. Musíme pritiahnuť frontendistov. Niekedy sa nám to zo začiatku nechce robiť. A SEO špecialisti povedia, že ak hľadáme obrázky, je im to jedno. A ak im poskytujeme nejaký obsah, povedia, že naše vyhľadávače nič neindexujú.

Čo s tým robiť? Samozrejme, budeme odovzdávať používateľovi HTML. Generovať ručne – nie je štýlové, preto chceme použiť šablóny. Na to existuje knižnica lua-resty-template.

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Určite ste si všimli tri desivé písmená OPM. OpenResty prichádza so svojím správcom balíkov, cez ktorý môžeme nainštalovať ešte množstvo rôznych modulov, konkrétne lua-resty-template. Je to jednoduchý šablónový engine, blízky Django šablónam. Tam môžeme napísať kód a urobiť substitúciu premenných.

V dôsledku toho to bude vyzerať zhruba takto:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Vzali sme dáta a zrenderovali šablónu opäť len na dve čiarky. Používateľ je šťastný, dostal mačičky. Keďže sme rozšírili požiadavku, dostal okrem mačiatok aj morského leva. Kto vie, možno práve jeho hľadal, ale nemohol správne formulovať svoj dopyt.

Všetko je skvelé, ale sme predsa v vývoji a zatiaľ nechceme používateľom ukazovať. Poďme spraviť autorizáciu. Aby sme to spravili, pozrime sa, ako NGINX spracováva požiadavku z pohľadu OpenResty:

  • Prvá fáza – access, keď používateľ práve prišiel, a my sme sa naňho pozreli podľa hlavičiek, podľa IP adresy a iných údajov. Môžeme ho okamžite odpojiť, ak sa nám nepáči. Toto môžeme použiť na autentifikáciu, alebo, ak nám prichádza veľa žiadostí, môžeme ich ľahko odmietnuť v tejto fáze.
  • rewrite. Prepisujeme nejaké údaje žiadosti.
  • content. Odovzdávame obsah používateľovi.
  • filter hlavičiek. Môžeme zmeniť hlavičky odpovede. Ak sme použili proxy_pass, môžeme prepísať niektoré hlavičky predtým, ako ich odovzdáme používateľovi.
  • filter tela. Môžeme zmeniť telo.
  • log — logovanie. Môžeme zapisovať logy do elasticsearch bez dodatočnej vrstvy.

Naša autentifikácia bude vyzerať približne takto:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Toto pridáme do toho location, ktoré sme opísali predtým, a vložíme tam takýto kód:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Pozrieme sa, či máme cookie token. Ak nie, odosielame na autentifikáciu. Používatelia sú prefíkaní a môžu uhádnuť, že je potrebné nastaviť cookie token. Preto to ešte uložíme aj do Redis:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Kód práce s Redis je veľmi jednoduchý a nelíši sa od iných jazykov. Všetok vstup/výstup tam aj tu je neblokujúci. Ak píšete synchronný kód, funguje to asynchrónne. Menej viac ako s gevent, ale spravené to je dobre.

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Poďme spraviť samotnú autentifikáciu:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Povieme, že potrebujeme čítať telo žiadosti. Získame POST argumenty, skontrolujeme, či je používateľské meno a heslo správne. Ak sú nesprávne, odosielame na autentifikáciu. A ak sú správne, ukladáme token do Redis:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Nezabudnite nastaviť cookie, to sa tiež robí v dvoch riadkoch:

OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Príklad je jednoduchý, teoretický. Samozrejme, že nebudeme robiť službu, ktorá ukazuje ľuďom mačky. Aj keď kto nás pozná. Preto poďme prejsť po tom, čo môžeme spraviť v produkcii.

  • Minimalistické backendové riešenie. Niekedy potrebujeme v backende vydať len pár údajov: niekde je potrebné dosadiť dátum, niekde zobraziť nejaký zoznam, povedať, koľko používateľov je teraz na stránke, pripojiť počítadlo alebo štatistiku. Niečo také malé. Minimálne kúsky je veľmi ľahké vytvoriť. Na tomto to bude rýchlo, jednoducho a skvele.
  • Preprocessing údajov. Niekedy máme chuť začleniť do našich stránok reklamu, pričom túto reklamu berieme cez API požiadavky. Je veľmi jednoduché to urobiť práve tu. Nezaťažujeme náš backend, ktorý už ťažko pracuje. Môžeme si to tu zbierať. Môžeme poskladať nejaké JS alebo, naopak, rozložiť, niečo preprocessovať pred tým, než to odovzdáme používateľovi.
  • Facade pre microservice. Toto je tiež veľmi dobrý prípad, ktorý som realizoval. Predtým som pracoval vo firme Tenzor, ktorá sa zaoberá elektronickým reportovaním a zabezpečuje reportovanie asi pre polovicu právnických osôb v krajine. Vytvorili sme službu, kde pomocou tohto mechanizmu bola realizovaná väčšina vecí: smerovanie, autorizácia a iné.
    OpenResty možno použiť ako lepidlo pre vaše microservisy, ktoré zabezpečí jednotný prístup ku všetkému a jedinému rozhraniu. Keďže microservisy môžu byť napísané tak, že tu máte Node.js, tu máte PHP, tu Python, a niekde beží niečo na Erlangu, chápeme, že nechceme prepisovať ten istý kód všade. Preto sa OpenResty dá zasadiť na front-end.
  • Štatistika a analýza. Zvyčajne NGINX stojí na vstupe a všetky požiadavky idú cez neho. Práve na tomto mieste je veľmi pohodlné zbierať dáta. Môžete niečo ihneď počítať a niekam poslať, napríklad do Elasticsearch, Logstash alebo len zaznamenať do logu a potom niekam odoslať.
  • Viacerí užívatelia v systéme. Napríklad online hry sa tiež veľmi dobre robia. Dnes v Kapskom Meste bude Alexander Gladýš rozprávať, ako rýchlo prototypovať multiplayerovú hru pomocou OpenResty.
  • Filtrovanie požiadaviek (WAF). Dnes je moderné robiť rôzne webové aplikačné firewally, existuje mnoho služieb, ktoré ich poskytujú. S pomocou OpenResty si možete vytvoriť webový aplikačný firewall, ktorý jednoducho a ľahko bude filtrovať požiadavky podľa vašich požiadaviek. Ak máte Python, chápete, že PHP vám určite nemôže prejsť injekciou, ak ho samozrejme nespauníte nikde z konzoly. Viete, že máte MySQL a Python. Pravdepodobne sa tu niekto môže pokúsiť urobiť nejaký directory traversal a niečo infiltrovať do databázy. Preto môžete rýchlo a lacno filtrovať podozrivé požiadavky priamo na front-end.
  • Komunita. Keďže OpenResty je postavený na NGINX, má bonus - to je NGINX-komunitaJe to veľmi rozsiahle a značná časť otázok, ktoré sa vám na začiatku môžu vyskytnúť, už bola vyriešená komunitou NGINX.

    Lua vývojáriVčera som hovoril s chlapcami, ktorí prišli na vzdelávací deň HighLoad++ a počul som, že na Lua je napísaný iba Tarantool. To nie je pravda, na Lua je napísaných veľa projektov. Príklady: OpenResty, XMPP server Prosody, herný engine Love2D, Lua sa používa v Warcraft a na iných miestach. Lua vývojárov je veľa, majú veľkú a ochotnú komunitu. Všetky moje otázky ohľadom Lua boli vyriešené v priebehu niekoľkých hodín. Keď napíšete na mailing list, doslova o pár minút už dostanete množstvo odpovedí, ktoré vám vysvetlia, čo a ako, čo k čomu. Je to naozaj skvelé. Bohužiaľ, nie všade je taká pohostinná duševná komunita.
    Na OpenResty je GitHub, kde môžete založiť issue, ak sa niečo pokazí. Existuje mailing list na Google Groups, kde sa dajú prebrať všeobecné otázky, existuje aj mailing list v čínštine — možno, že anglicky nehovoríte, ale májete vedomosti v čínštine.

Zhrnutie

  • Dúfam, že som dokázal preniesť, že OpenResty je veľmi pohodlný framework, prispôsobený na web.
  • Má nízky prah vstupu, pretože kód je podobný tomu, na čom píšeme, jazyk je dosť jednoduchý a minimalistický.
  • Poskytuje asynchrónny I/O bez callbackov, nebudeme mať žiadne zamotané riešenia, ako sa to občas môže stať v NodeJS.
  • Má jednoduché nasadenie, pretože potrebujeme len NGINX s požadovaným modulom a náš kód a všetko hneď funguje.
  • Veľká a ochotná komunita.

Nepovedal som podrobnosti o tom, ako sa robí smerovanie, výsledkom by bol veľmi dlhý príbeh.

Ďakujem za pozornosť!

Prehrajte video

Vladimir Protasov — OpenResty: premeníme NGINX na plnohodnotný aplikačný server

Zdroj: habr.com
Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster