Ponovno objavljamo transkript izlaganja sa konferencije 2016. koja se održala u moskovskom Skolkovu 7—8. novembra prošle godine. govori o tome kako proširiti funkcionalnost NGINX-a pomoću OpenResty i Lua.
Zdravo svima, ja sam Vladimir Protasov, radim u Parallels-u. Reći ću malo o sebi. Tri četvrtine svog života se bavim kodiranjem. Postao sam programer doslovno do kostiju: ponekad u snu vidim kod. Četvrtinu života posvetio sam industrijskoj proizvodnji, pisanju koda koji ide direktno u produkciju. Kod kojim neki od vas koriste, ali ne znaju o njemu.
Da razumete koliko je sve bilo loše. Kada sam bio mali junior, došao sam i dobio takve dvotera bajtne baze. Sada svi imamo highload. Išao sam na konferencije, pitao: „Rao, ispričajte nam, imate big data, sve je super? Koliko imate baze?” Odgovarali su mi: „Imamo 100 gigabajta!” Govorio sam: „Super, 100 gigabajta!” A u sebi sam mislio, kako da zadržim poker face. Misliš, da, momci su sjajni, a zatim se vraćaš i mučiš se s tim višetera bajtnim bazama. A to je bilo kao junior. Možete li zamisliti kakav je to udarac?
Poznajem više od 20 programskih jezika. To je ono što sam morao da savladam tokom rada. Dobiješ kod na Erlangu, C, C++, Lua, Python-u, Ruby-u, nečemu drugom, i treba da sve to sastaviš. Uopšte, bilo je potrebno. Tačan broj nisam uspeo da izračunam, ali negde oko 20 sam se izgubio.
Budući da svi prisutni znaju šta je Parallels i čime se bavimo, neću govoriti o tome koliko smo sjajni i šta radimo. Samo ću reći da imamo 13 kancelarija širom sveta, više od 300 zaposlenih, razvoj u Moskvi, Talinu i na Malti. Ako želite, možete se preseliti na Maltu, ako je zima hladna i trebate da se ugrijete.
Naš odeljak konkretno piše na Python 2. Bavimo se poslovanjem i nemamo vremena za uvođenje modernih tehnologija, zato patimo. Imamo Django, jer u njemu ima svega, a suvišno smo izbacili. Takođe MySQL, Redis i NGINX. Imamo mnogo drugih sjajnih stvari. Imamo MongoDB, imamo zečeve koji trče, imamo svega—ali to nije moje i time se ne bavim.
OpenResty
O sebi sam rekao. Hajde da vidimo o čemu ću danas govoriti:
- Šta je OpenResty i s čim se jede?
- Zakaj izumljati še en kolo, ko imamo Python, NodeJS, PHP, Go in druge odlične stvari, s katerimi so vsi zadovoljni?
- In nekaj primerov iz življenja. Moral sem močno skrajšati poročilo, ker je trajalo 3,5 ure, zato bo primerov malo.
OpenResty je NGINX. Zahvaljujoč njemu imamo popoln spletni strežnik, ki je dobro napisan in deluje hitro. Mislim, da večina med nami uporablja NGINX v produkciji. Vsi veste, da je hiter in odličen. Ima odlično sinhrono vhod/izhod, zato nam ni treba izumljati kolesa, kot je to storjeno v Pythonu s gevent. Gevent je odličen, super, vendar če napišete C kodo in se kaj zalomi, se boste zaradi debagiranja z geventom zmešali. Imao sem izkušnjo: potreboval sem kar dva dni, da sem ugotovil, kaj je šlo narobe. Če ne bi nekdo pred tem raziskoval več tednov, našel težavo, napisal na spletu, in Google tega ne bi našel, bi se resnično zmešali.
V NGINX so že vključeni predpomnjenje in statična vsebina. Ni vam treba skrbeti, kako to narediti pravilno, da vas kje ne bi upočasnilo, da ne bi izgubili descriptorjev. Nginx je zelo enostaven za namestitev, ni vam treba razmišljati, kaj vzeti – WSGI, PHP-FPM, Gunicorn, Unicorn. Nginx namestite, predate administratorjem, oni vedo, kako to delati. Nginx strukturirano obravnava zahteve. O tem bom malo pozneje govoril. Na kratko ima fazo, ko sprejme zahtevo, ko jo obdeluje in ko vrne vsebino uporabniku.
Nginx je super, vendar obstaja en problem: ni dovolj prilagodljiv, kljub vsem tistim odličnim funkcijam, ki so jih fantje vgradili v konfiguracijo, čeprav jo je mogoče prilagoditi. Te moči primanjkuje. Zato so fantje iz Taobao pred osmimi leti vgradili Lua. Kaj to prinaša?
- Dimenzije. Je majhen. LuaJIT daje nekje 100-200 kilobajtov overheada pomnilnika in minimalen overhead pri zmogljivosti.
- Hitrost. Interpreter LuaJIT je v mnogih situacijah blizu C, v nekaterih ga premaga Java, v nekaterih pa jo prehiti. Nekaj časa je bil cenjen kot state of the art, najboljši JIT kompajler. Zdaj so boljši, vendar so zelo težki, na primer V8. Nekateri JS interpreterji in Java HotSpot so v nekaterih točkah hitrejši, a v nekaterih še vedno zaostajajo.
- Enostavno za učenje. Če imate, recimo, kodo na Perlu in niste Booking, ne boste našli PerlProgramerjev. Ker jih ni, vse so jih pobrali, njihovi so dolgotrajni in zapleteni. Če želite programerje na nečem drugem, jih bo morda tudi potrebno ponovno usposobiti ali najti. V primeru Lua je vse enostavno. Lua se vsakdo nauči v treh dneh. Potreboval sem približno dve uri, da sem se ujel. Po dveh urah sem že pisal kodo v produkciji. Nekje po enem tednu je odšla naravnost v produkcijo.
Zato to izgleda takole:

Tu je veliko stvari. V OpenResty so zbrali kup modulov, tako luovskih kot endžinskih. In imate vse pripravljeno – razporedite in dela.
Primeri
Konec lirike, preidimo na kodo. Tukaj je majhen Hello World:

Kaj je tukaj? To je endžinsko mesto. Ne mučimo se, ne pišemo svojega usmerjanja, ne vzamemo kakšnega pripravljenega – že imamo v NGINX, živimo dobro in leno.
content_by_lua_block – to je blok, ki pove, da vsebino predajamo preko Lua skripte. Vzamemo endžinsko spremenljivko remote_addr in jo vstavi v string.format. To je isto kot sprintf, samo na Luu, samo prav. In predajamo stranki.
Zato bo to izgledalo takole:

Ampak vrnimo se v pravi svet. V produkciji nihče ne razporeja Hello World. Naša aplikacija običajno komunicira z bazo ali drugam in večino časa čaka na odgovor.

Preprosto sedi in čaka. To ni najboljše. Ko pride 100.000 uporabnikov, nam je zelo težko. Zato si poglejmo preprosto aplikacijo. Iščevali bomo slike, na primer mačk. Samo ne bomo preprosto iskali, razširili bomo ključne besede in, če uporabnik išče 'mačičke', mu bomo našli mačke, puhaste in podobno. Najprej moramo pridobiti podatke o zahtevi na zadnjem delu. Izgleda to takole:

Dve vrstici vam omogočata, da pridobite GET parametre, brez zapletenosti. Nato iz baze podatkov s tabelo o ključni besedi in razširitvi pridobimo te informacije s preprostim SQL poizvedovanjem. Vse je preprosto. Izgleda takole:

Povezujemo knjižnico resty.mysql, ki jo že imamo v kompletu. Nič ne potrebujemo namestiti, vse je pripravljeno. Navedemo, kako se povezati in izvedemo SQL poizvedbo:

Tu je malo strašljivo, a sve funkcioniše. Ovde je 10 – to je limit. Uzimamo 10 zapisa, lenji smo, ne želimo da prikazujemo više. Zaboravio sam na limit u SQL-u.
Dalje, nalazimo slike za sve upite. Prikupljamo gomilu upita i popunjavamo Lua tabelu koja se zove reqs, i radimo ngx.location.capture_multi.

Svi ti upiti idu paralelno, i dobijamo odgovore. Vreme rada je jednako vremenu odgovora najsporijeg. Ako svi postignu za 50 milisekundi, a poslali smo stotinu upita, odgovor će nam stići za 50 milisekundi.
Pošto smo lenji i ne želimo da pišemo HTTP obradu i keširanje, nateraćemo NGINX da sve uradi umesto nas. Kao što ste videli, bio je upit na url/fetch, evo ga:

Radimo jednostavno proxy_pass, navodimo gde da se kešira, kako to uraditi, i sve funkcioniše.
Ali to nije dovoljno, još moramo da damo podatke korisniku. Najjednostavnija ideja je sve serijalizovati u JSON, lako, u dve linije. Davamo Content-Type, dajemo JSON.
Ali postoji jedan problem: korisnik ne želi da čita JSON. Moramo da angažujemo frontendere. Ponekad nam se na početku ne sviđa da to radimo. A i SEO stručnjaci će reći da im nije bitno ako tražimo slike. A ako im dajemo neki sadržaj, reći će da pretraživači ništa ne indeksiraju.
Šta uraditi s tim? Normalno, daćemo korisniku HTML. Ručno generisanje – nije u redu, zato želimo da koristimo šablone. Za to postoji biblioteka lua-resty-template.

Verovatno ste primetili tri strašne slova OPM. OpenResty dolazi sa svojim paket menadžerom, preko kojeg možete instalirati još mnoštvo raznih modula, uključujući lua-resty-template. To je jednostavan šablon motor, sličan Django šablonima. Tamo možete napisati kod i zameniti promenljive.
Kao rezultat, sve će izgledati otprilike ovako:

Uzimamo podatke i renderujemo šablon opet u dve linije. Korisnik je srećan, dobio je mačiće. Pošto smo proširili upit, dobio je i morskog lava. Možda je tražio baš njega, ali nije mogao pravilno da formuliše svoj upit.
Sve je sjajno, ali smo u razvoju i ne želimo da prikazujemo korisnicima. Hajde da uradimo autorizaciju. Da bismo to uradili, hajde da vidimo kako NGINX obrađuje upit u terminima OpenResty:
- Prva faza – access, ko uporabnik šele pride, ga lahko takoj preučimo po naslovih, IP naslovu in drugih podatkih. Če nam ni všeč, ga lahko takoj blokiramo. To se lahko uporablja za avtentikacijo, ali pa, če prejmemo veliko zahtevkov, jih lahko enostavno zavrnemo v tej fazi.
- rewrite. Prepisujemo nekatere podatke zahtevka.
- content. Vrnemo vsebino uporabniku.
- filtriranje glavic. Zamenjamo glavice odgovora. Če smo uporabili
proxy_pass, lahko prepišemo nekatere glavice, preden jih vrnemo uporabniku. - filtriranje telesa. Lahko zamenjamo telo.
- dnevnik — beleženje. Beležimo lahko dnevnike v elasticsearch brez dodatne plasti.
Naša avtentikacija bo izgledala približno tako:

To dodamo v tisti lokacija, ki smo ga opisali prej, in vanj vstavimo takšno kodo:

Poglejmo, ali imamo cookie token. Če ne, uporabnike usmerimo na avtentikacijo. Uporabniki so spretni in se lahko domislijo, da morajo dodati cookie token. Zato jo še dodamo v Redis:

Koda za delo z Redisom je zelo preprosta in se ne razlikuje od drugih jezikov. Vsi vhodni/izhodni podatki tam in tukaj niso blokirni. Če pišete sinhrono kodo, ta deluje asinhrono. Tako kot pri gevent, le da je to dobro narejeno.

Poglejmo, kako izvedemo avtentikacijo:

Povezujemo, da moramo prebrati telo zahtevka. Prejmemo POST argumente, preverimo, ali sta uporabniško ime in geslo pravilna. Če nista, jih usmerimo na avtentikacijo. Če sta pravilna, zapišemo token v Redis:

Ne pozabimo dodati cookieja, to se naredi v dveh vrsticah:

Primer je preprost, teoretičen. Seveda ne bomo ustvarjali storitve, ki prikazuje ljudem mačke. Kdo ve. Zato poglejmo, kaj lahko naredimo v produkciji.
- Minimalistična nazadnja. Včasih potrebujemo, da nazadnja vrne zelo malo podatkov: kje je treba postaviti datum, kje prikazati kakšen seznam, povedati, koliko uporabnikov je trenutno na spletnem mestu, priložiti števec ali statistiko. Nekaj takega majhnega. Minimalni koščki se lahko zelo enostavno izdelajo. S tem bo hitro, enostavno in dobro.
- Predobdelava podatkov. Včasih si želimo vključiti oglaševanje na našo stran, pri čemer to oglaševanje pridobivamo preko API poizvedb. To je zelo enostavno narediti tukaj. Ne obremenjujemo našega backend-a, ki že tako trdo dela. Lahko pa to sestavimo tukaj. Lahko naredimo kakšen JS ali pa ravno obratno, kaj predelamo, preden to posredujemo uporabniku.
- Facade za mikroservis. To je prav tako zelo dobra zadeva, jo je že izvedel. Pred tem sem delal v podjetju Tenzor, ki se ukvarja z elektronskim poročanjem, in zagotavlja poročanje približno polovici pravnih oseb v državi. Naredili smo storitev, kjer je bilo s pomočjo istega mehanizma izvedenih mnogo stvari: usmerjanje, avtentikacija in drugo.
OpenResty lahko uporabite kot vezivo za vaše mikroservise, ki bo zagotovil enoten dostop do vsega in enoten vmesnik. Ker so mikroservisi lahko napisani tako, da imate tu Node.js, tam PHP, tam Python, in tam je lahko kaj na Erlangu, razumemo, da ne želimo istega kode pretvarjati povsod. Zato lahko OpenResty postavite na sprednji del. - Statistika in analiza. Običajno NGINX stoji na vhodu in vse poizvedbe gredo preko njega. Prav na tem mestu je zelo priročno zbirati. Lahko nekaj neposredno izračunate in nekam pošljete, na primer Elasticsearch, Logstash ali pa preprosto zapišete v dnevnik in ga kasneje kam pošljete.
- Večuporabniški sistemi. Na primer, spletne igre je prav tako zelo dobro razvijati. Danes bo v Cape Townu Aleksander Gladish govoril o tem, kako hitro prototipirati večuporabniško igro z uporabo OpenResty.
- Filtracija poizvedb (WAF). Zdaj je moderno delati različne požarne zidove za spletne aplikacije, je veliko storitev, ki jih zagotavljajo. S pomočjo OpenResty lahko ustvarite svoj spletni požarni zid, ki bo preprosto in enostavno filtriral poizvedbe po vaših zahtevah. Če imate Python, potem razumete, da vam PHP zagotovo ne morejo injicirati, razen če seveda ga ne zaženete iz konzole nikjer. Veste, da imate MySQL in Python. Verjetno bodo tu poskusili narediti kakšno prečkanje direktorija in kaj injicirati v bazo. Zato lahko hitro in poceni filtrirate slabih poizvedb takoj na sprednjem delu.
- Skupnost. Ker je OpenResty zgrajen na osnovi NGINX, ima še dodatek - to je NGINX skupnostJe zelo obsežen in znaten del vprašanj, ki se vam lahko sprva postavi, je že rešen s strani skupnosti NGINX.
Lua razvijalciVčeraj sem se pogovarjal z ljudmi, ki so prišli na izobraževalni dan HighLoad++, in slišal, da je le Tarantool napisan v Lua. To ni res, v Lua je napisanih veliko stvari. Primeri: OpenResty, strežnik XMPP Prosody, igralni motor Love2D, Lua se uporablja v Warcraftu in na drugih mestih. Lua razvijalcev je zelo veliko, imajo veliko in odzivno skupnost. Vsa moja vprašanja o Lua so bila rešena v nekaj urah. Ko pišem na mailing listo, dobiš v dobesedno nekaj minutah kup odgovorov, ki podrobno razložijo, kako in kaj, in kaj je s čim. To je res odlično. Žal pa ni povsod tako dobrosrčna skupnost.
Na voljo je GitHub za OpenResty, kjer lahko odprete težavo, če se kaj pokvari. Obstaja mailing lista na Google Groups, kjer se lahko razpravljajo splošna vprašanja, obstaja tudi mailing lista v kitajščini - če slučajno ne obvladate angleščine, morda pa znate kitajsko.
Zaključki
- Upam, da sem uspel sporočiti, da je OpenResty zelo priročen okvir, prilagojen za splet.
- Njegov vstopni prag je nizek, saj je koda podobna tistemu, kar pišemo, jezik je precej preprost in minimalističen.
- Ponuja asinhronski I/O brez povratnih klicev, ne bomo imeli 'testenin', kot jih včasih napišemo v NodeJS.
- Ima preprosto namestitev, saj potrebujemo le NGINX z ustreznim modulom in našo kodo, in že vse deluje.
- Velika in odzivna skupnost.
Nisem podrobno opisal, kako se izvaja usmerjanje, ker bi to bilo zelo dolgo pripovedovanje.
Hvala za pozornost!

Izvor: habr.com
