OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

OpenResty: proměňujeme NGINX v plnohodnotný aplikační serverOpět zveřejňujeme přepis prezentace z konference HighLoad++ 2016, která proběhla v podmoskevském Skolkovo 7.–8. listopadu loňského roku. Vladimir Protasov vypráví, jak rozšířit funkčnost NGINX pomocí OpenResty a Lua.

Ahoj všichni, jmenuji se Vladimir Protasov, pracuji ve společnosti Parallels. O sobě řeknu trochu víc. Tři čtvrtiny svého života se zabývám programováním. Stal jsem se programátorem ve skutečném slova smyslu: někdy ve spánku vidím kód. Čtvrtinu svého života strávím v průmyslovém vývoji, psaním kódu, který jde přímo do produkce. Kód, který někteří z vás používají, ale ani netuší, že jej používají.

Abychom si plně uvědomili, jak špatné to bylo. Když jsem byl malý junior, přišel jsem a dostal takové dvouterabajtové databáze. Teď má každý vysoké zatížení. Chodil jsem na konference, ptal se: "Chlapi, vy máte big data, všechno je úžasné? Kolik máte takových databází?" Oni odpovídali: "Máme 100 gigabajtů!" Říkal jsem: "Úžasné, 100 gigabajtů!" A přitom jsem si v duchu říkal, jak udržet poker face. Myslíte, že jsou šikovní chlapi, a pak se vrátíte a šťouráte se s těmi víceterabajtovými databázemi. A to vše jako junior. Umíte si představit, jaký to byl zásah?

Ovládám více než 20 programovacích jazyků. To je to, čemu jsem se musel naučit během své práce. Dostanete kód v Erlangu, C, C++, Lua, Pythonu, Ruby a dalším a musíte s tím vším pracovat. Takže to bylo nutné. Přesný počet se mně zatím nepodařilo spočítat, ale někde jsem se ztratil na čísle 20.

Protože všichni přítomní vědí, co je Parallels a čím se zabýváme, nebudu mluvit o tom, jak jsme skvělí a co děláme. Jen řeknu, že máme 13 kanceláří po celém světě, více než 300 zaměstnanců, vývoj v Moskvě, Tallinnu a na Maltě. Pokud chcete, můžete se přestěhovat na Maltu, pokud je v zimě chladno a potřebujete zahřát záda.

Konkrétně naše oddělení píše v Pythonu 2. Zabýváme se byznysem a nemáme čas na to, abychom implementovali moderní technologie, a proto trpíme. Máme Django, protože v něm je všechno, co potřebujeme, a nadbytečné věci jsme vyhodili. Také MySQL, Redis a NGINX. Dále máme spoustu jiných skvělých věcí. Máme MongoDB, máme králíky, máme toho tolik – ale to není má doména, tomu se nevěnuji.

OpenResty

O sobě jsem již řekl. Pojďme se podívat na to, o čem budu dnes mluvit:

  • Co je to OpenResty a k čemu se používá?
  • Proč vymýšlet další kolo, když máme Python, NodeJS, PHP, Go a další skvělé nástroje, kterými jsou všichni spokojeni?
  • A teď pár příkladů z praxe. Musel jsem hodně zkrátit svou prezentaci, protože trvala 3,5 hodiny, takže příkladů bude málo.

OpenResty je NGINX. Díky němu máme plnohodnotný webový server, který je dobře napsaný a rychle funguje. Myslím, že většina z nás používá NGINX v produkci. Všichni víte, že je rychlý a skvělý. Má skvělou synchronní I/O, takže nic dalšího nemusíme vymýšlet, jako to udělali v Pythonu s gevent. Gevent je skvělý, úžasný, ale když napíšete kód v C a něco se pokazí, tak vás s geventem šíleně vykolejí jeho ladění. Měl jsem takovou zkušenost: potřeboval jsem celé dva dny na to, abych zjistil, co se pokazilo. Kdyby to někdo před tím nekontroloval několik týdnů, nenalezl problém, nenapsal o tom na internetu a Google to nenašel, tak bychom se úplně zbláznili.

V NGINX už je implementováno caching a statický obsah. Nemusíte se trápit nad tím, jak to udělat správně, aby se někde nezasekávalo, abyste někde neztratili deskriptory. Nginx je velmi snadno nasaditelný, nemusíte přemýšlet, co použít – WSGI, PHP-FPM, Gunicorn, Unicorn. Nginx nainstalujete, předáte administrátorům, oni vědí, jak s tím pracovat. Nginx strukturovaně zpracovává požadavky. O tom budu mluvit později. Stručně má fázi, kdy pouze přijal požadavek, kdy ho zpracoval a kdy odeslal obsah uživateli.

Nginx je skvělý, ale má jeden problém: není dostatečně flexibilní i při všech těch skvělých funkcích, které kluci vměstnali do konfigurace, ačkoli lze nastavit. Tato síla mu chybí. Proto kluci z Taobao před osmi lety někdy integrovali Lua. Co to přináší?

  • Velikost. Je malý. LuaJIT dává přibližně 100-200 kilobajtů paměťového overheadu a minimální overhead v oblasti výkonu.
  • Rychlost. Interpreter LuaJIT je v mnoha situacích blízký C, v některých případech ztrácí na Java a v některých ji překonává. Někdy byl považován za state of the art, nejlepší JIT kompilátor. Nyní existují lepší, ale jsou velmi těžké, například V8. Některé JS interpretery a Java HotSpot jsou v určitých situacích rychlejší, ale v některých ohledech stále zaostávají.
  • Jednoduchost učení. Pokud máte například kódovou základnu v Perlu a nejste Booking, nenajdete programátory v Perlu. Protože není – všichni byli odejiti, a jejich výuka trvá dlouho a je složitá. Pokud chcete programátory v něčem jiném, možná je také budete muset přeškolit nebo najít. V případě Lua je to jednoduché. Lua se jakýkoli junior naučí za tři dny. Potřeboval jsem asi dvě hodiny, abych se zorientoval. Po dvou hodinách jsem už psal kód do produkce. Přibližně za týden šel přímo do produkce.

Výsledkem to vypadá takto:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Je zde mnoho věcí. V OpenResty shromáždili spoustu modulů, jak luajských, tak i nginxových. A máte vše hotové – nasadili jste a funguje to.

Příklady

Dost bylo lyriky, přejdeme kódu. Tady je malý Hello World:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Co zde máme? Toto je nginxové location. Nemusíme se trápit, nepíšeme vlastní routování, nebereme žádné hotové – máme již v NGINX, žijeme dobře a líně.

content_by_lua_block – to je blok, který říká, že poskytujeme obsah pomocí Lua skriptu. Bereme nginxovou proměnnou remote_addr a předáváme ji do string.format. To je totéž, co sprintf, pouze na Lua, pouze správně. A posíláme ji klientovi.

V důsledku toho to bude vypadat takto:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Ale vraťme se do skutečného světa. Do produkce nikdo nasazuje Hello World. Naše aplikace obvykle chodí do databáze nebo někam jinam a většinu času čeká na odpověď.

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Jednoduše sedí a čeká. To není moc dobré. Když přijde 100.000 uživatelů, máme to velmi těžké. Proto si v příkladu vytvoříme jednoduchou aplikaci. Budeme hledat obrázky, například kočky. Jenže nebudeme hledat jen tak, budeme rozšiřovat klíčová slova a pokud uživatel hledal „koťata“, najdeme mu kočky, chlupatce a další. Nejdříve potřebujeme získat data dotazu na backendu. Vypadá to takto:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Dva řádky vám umožní zobrat GET parametry, žádné složitosti. Dále například získáváme tuto informaci z databáze s tabulkou podle klíčového slova a přípony běžným SQL dotazem. Všechno je jednoduché. Vypadá to takto:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Připojujeme knihovnu resty.mysql, která už máme v balíčku. Nic nemusíme instalovat, všechno je hotové. Uvádíme, jak se připojit, a děláme SQL dotaz:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Tady je to trochu děsivé, ale všechno funguje. Zde 10 - to je limit. Získáváme 10 záznamů, jsme líní, nechceme zobrazovat více. Na limit v SQL jsem zapomněl.

Dále hledáme obrázky podle všech dotazů. Shromažďujeme sérii dotazů a vyplňujeme Lua tabulku, která se nazývá reqs, a provádíme ngx.location.capture_multi.

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Všechny tyto dotazy jdou paralelně a vrací se nám odpovědi. Čas zpracování je roven času odpovědi nejpomalejšího. Pokud se nám všechny vrátí během 50 milisekund, a poslali jsme stovku dotazů, pak odpověď přijde za 50 milisekund.

Protože jsme líní a nechceme psát zpracování HTTP a cachování, přinútíme NGINX, aby to všechno udělal za nás. Jak jste viděli, byl tam dotaz na url/fetch, to je ono:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Děláme jednoduché proxy_pass, uvádíme, kam ukládat do cache, jak to udělat, a všechno nám funguje.

Ale to nestačí, musíme také vrátit data uživateli. Nejjednodušší myšlenka - to vše serializovat do JSON, snadno, ve dvou řádcích. Vracíme Content-Type, vracíme JSON.

Ale je tu jedna komplikace: uživatel nechce číst JSON. Je třeba přitáhnout frontendové vývojáře. Někdy se nám to na začátku nechce dělat. A SEO specialisté řeknou, že pokud hledáme obrázky, je to jim jedno. Ale pokud nějaký obsah předáváme, řeknou, že naše vyhledávače nic neindexují.

Co s tím dělat? Samozřejmě, že budeme uživateli vracet HTML. Generovat to ručně - není to stylové, takže chceme použít šablony. K tomu je tady knihovna lua-resty-template.

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Určitě jste si všimli tří děsivých písmen OPM. OpenResty přichází se svým balíčkovým manažerem, přes který lze nainstalovat ještě spoustu různých modulů, konkrétně lua-resty-template. Je to jednoduchý šablonový engine, blízký Django templates. Můžete napsat kód a provést substituci proměnných.

Výsledkem bude vše vypadat přibližně takto:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Údaje jsme vzali a zrenderovali šablonu opět ve dvou řádcích. Uživatel je spokojený, dostal kočky. Jelikož jsme rozšířili dotaz, dostal k kittenům navíc ještě i mořského lva. Kdo ví, možná ho hledal právě tohoto, ale nedokázal správně formulovat svůj dotaz.

Všechno je skvělé, ale jsme přece ve vývoji a zatím nechceme uživatelům ukazovat. Udělejme tedy autorizaci. Abychom to provedli, podívejme se, jak NGINX zpracovává požadavek v termínech OpenResty:

  • První fáze — access, když uživatel právě přišel a my jsme se na něj podívali podle hlaviček, podle IP adresy, podle dalších údajů. Můžeme ho okamžitě odříznout, pokud se nám nelíbí. To lze použít pro autorizaci, nebo pokud k nám přichází spousta požadavků, můžeme je snadno odříznout v této fázi.
  • rewrite. Přepisujeme nějaké údaje požadavku.
  • content. Dodáváme obsah uživateli.
  • headers filter. Měníme záhlaví odpovědi. Pokud jsme použili proxy_pass, můžeme přepsat některá záhlaví, než je dodáme uživateli.
  • body filter. Můžeme změnit tělo.
  • log — protokolování. Můžeme zapisovat protokoly do elasticsearch bez další vrstvy.

Naše autorizace bude vypadat zhruba takto:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Toto přidáme do toho location, které jsme popsali dříve, a vložíme tam takový kód:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Podíváme se, jestli máme cookie token. Pokud ne, posíláme na autorizaci. Uživatelé jsou mazaní a mohou uhodnout, že je potřeba nastavit cookie token. Proto ji ještě uložíme do Redis:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Kód pro práci s Redis je velmi jednoduchý a nijak se neliší od ostatních jazyků. Všechny vstupy/výstupy jsou jak tam, tak i tady, neblokující. Pokud píšete synchronní kód, funguje asynchronně. Přibližně jako s gevent, jen to je uděláno dobře.

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Uděláme tedy samotnou autorizaci:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Řekneme, že potřebujeme číst tělo požadavku. Získáme POST argumenty, zkontrolujeme, že přihlašovací jméno a heslo jsou správné. Pokud nejsou správné, posíláme na autorizaci. A pokud jsou správné, zapisujeme token do Redis:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Nezapomínejte nastavit cookie, to se také dělá ve dvou řádcích:

OpenResty: proměňujeme NGINX v plnohodnotný aplikační server

Příklad je jednoduchý, teoretický. Samozřejmě nebudeme dělat službu, která lidem ukazuje kočky. Ačkoli kdo ví. Takže projdeme, co lze udělat v produkci.

  • Minimalistický backend. Někdy potřebujeme v backendu poskytnout jen malé množství dat: někde je potřeba doplnit datum, někde vypsat nějaký seznam, říct, kolik je teď uživatelů na webu, připojit počítadlo nebo statistiku. Něco takového menšího. Minimální kousky lze udělat velmi snadno. Na tom to půjde rychle, snadno a skvěle.
  • Předzpracování dat. Někdy bychom chtěli integrovat do naší stránky reklamu, přičemž tuto reklamu bereme pomocí API dotazů. To se dá udělat právě tady velmi snadno. Nezátěžujeme náš backend, který už tak těžce pracuje. Můžeme vzít a sestavit to tady. Můžeme slepit nějaké JS nebo naopak rozdělit, něco předzpracovat, než to předáme uživateli.
  • Fasáda pro mikroservis. To je také velmi dobrý případ, který jsem realizoval. Předtím jsem pracoval ve společnosti Tenzor, která se zabývá elektronickým účetnictvím a zajišťuje účetnictví přibližně poloviny právnických osob v zemi. Vytvořili jsme službu, kde pomocí tohoto mechanizmu bylo uděláno mnoho věcí: směrování, autorizace a další.
    OpenResty lze použít jako lepidlo pro vaše mikroservisy, které zajistí jednotný přístup ke všemu a jednotní rozhraní. Vzhledem k tomu, že mikroservisy mohou být napsány různě, zde máte Node.js, zde PHP, zde Python, zde něco na Erlangu, chápeme, že nechceme přepisovat stejný kód všude. Proto lze OpenResty nasadit na frontend.
  • Statistika a analytika. Obvykle je NGINX na vstupu a všechny dotazy jdou přes něj. Právě v tomto místě je velmi pohodlné shromáždit data. Můžeme něco hned spočítat a někam to poslat, například do Elasticsearch, Logstash, nebo jen zapsat do logu a pak poslat někam jinam.
  • Multiplayerové systémy. Například online hry se také velmi dobře dělají. Dnes v Kapském Městě bude Alexandr Hladyš vyprávět, jak rychle prototypovat multiplayerovou hru pomocí OpenResty.
  • Filtrace dotazů (WAF). V dnešní době je módou vytvářet různé webové aplikační firewally, existuje mnoho služeb, které je poskytují. Pomocí OpenResty můžete vytvořit webový aplikační firewall, který snadno a efektivně filtruje požadavky podle vašich potřeb. Pokud máte Python, víte, že PHP nelze snadno injektovat, pokud ho samozřejmě nespustíte z konzole. Víte, že máte MySQL a Python. Pravděpodobně se mohou pokusit provést nějaký directory traversal a injektovat něco do databáze. Proto je možné rychle a levně filtrovat podezřelé požadavky přímo na front-endu.
  • Komunita. Jelikož je OpenResty postaven na NGINX, má bonus – NGINX komunitu. Ta je velmi rozsáhlá a značná část otázek, které se vám zpočátku objeví, již byla vyřešena komunitou NGINX.

    Lua vývojáři. Včera jsem mluvil s lidmi, kteří přišli na vzdělávací den HighLoad++, a slyšel jsem, že na Lua je napsán pouze Tarantool. To ale není pravda, na Lua je napsáno hodně věcí. Příklady: OpenResty, XMPP server Prosody, herní engine Love2D, Lua může být skriptována ve Warcraftu a na jiných místech. Lua vývojářů je opravdu hodně, mají velkou a aktivní komunitu. Všechny mé dotazy ohledně Lua byly zodpovězeny během několika hodin. Když napíšete na mailing list, doslova za několik minut už je spousta odpovědí, podrobně popisují, co a jak, co k čemu. To je skvělé. Bohužel, ne všude je tak laskavá a vstřícná komunita.
    K OpenResty existuje GitHub, kde můžete založit issue, pokud něco nefunguje. Existuje mailing list na Google Groups, kde můžete diskutovat o obecných otázkách, a je také mailing list v čínštině – kdo ví, možná anglicky nevládnete, ale čínsky znáte.

Závěry

  • Doufám, že jsem dokázal sdělit, že OpenResty je velmi pohodlný framework zaměřený na web.
  • Má nízký práh vstupu, protože kód se podobá tomu, na čem píšeme, jazyk je poměrně jednoduchý a minimalistický.
  • Nabízí asynchronní I/O bez callbacků, nebudeme mít nudli, jak ji někdy můžeme napsat v NodeJS.
  • Má jednoduché nasazení, protože potřebujeme pouze NGINX s potřebným modulem a náš kód, a vše funguje hned.
  • Velká a vstřícná komunita.

Nedokázal jsem podrobně popsat, jak se dělá routování, dostalo by se to do velmi dlouhého vyprávění.

Děkujeme za pozornost!

Přehrát video

Vladimir Protasov – OpenResty: měníme NGINX na plnohodnotný aplikační server

Zdroj: habr.com
Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster