Ponovno objavljujemo prijepis izvješća sa skupa 2016. koji se 7. i 8. studenog prošle godine održao u podmoskovskom Skolkovu. objašnjava kako proširiti funkcionalnost NGINX-a s OpenResty i Lua.
Pozdrav svima, moje ime je Vladimir Protasov, radim u Parallelsu. Reći ću vam nešto o sebi. Proveo sam tri četvrtine svog života pišući kod. Postao sam programer do srži u doslovnom smislu: ponekad vidim kod u svojim snovima. Četvrtina života je industrijski razvoj, pisanje koda koji ide ravno u proizvodnju. Kod koji neki od vas koriste, ali ga ne shvaćaju.
Pa shvaćate koliko je bilo loše. Kad sam bio mali, došao sam i dobio sam te baze podataka od dva terabajta. Ovdje je sada veliko opterećenje za sve. Išao sam na konferencije i pitao: “Dečki, recite mi, imate velike podatke, je li sve cool? Koliko baza imate tamo? Odgovorili su mi: "Imamo 100 gigabajta!" Rekao sam: "Kul, 100 gigabajta!" I razmišljao sam u sebi kako pažljivo održavati svoje pokeraško lice. Pomisliš, da, dečki su cool, a onda se vratiš i petljaš s tim bazama podataka od više terabajta. A ovo je biti junior. Možete li zamisliti kakav je ovo udarac?
Poznajem više od 20 programskih jezika. To je nešto što sam morao shvatiti dok sam radio. Daju vam kod u Erlangu, C, C++, Lua, Python, Ruby, nešto drugo, a vi to sve morate rezati. Općenito, morao sam. Točan broj nije bilo moguće izračunati, ali negdje oko 20. broj se izgubio.
Budući da svi prisutni znaju što su Paralele i čime se bavimo, neću pričati o tome koliko smo cool i što radimo. Samo ću vam reći da imamo 13 ureda diljem svijeta, više od 300 zaposlenika, razvoj u Moskvi, Tallinnu i na Malti. Ako želite, možete uzeti i preseliti se na Maltu ako je zimi hladno i trebate ugrijati leđa.
Konkretno, naš odjel piše u Pythonu 2. Mi smo u poslu i nemamo vremena implementirati moderne tehnologije, pa patimo. Koristimo Django jer ima svega, a nepotrebno smo uzeli i bacili. Također MySQL, Redis i NGINX. Imamo i puno drugih cool stvari. Imamo MongoDB, imamo zečeve koji trče uokolo, imamo sve - ali to nije moje i ja to ne radim.
OpenResty
Rekao sam o sebi. Smislimo o čemu ću danas govoriti:
- Što je OpenResty i s čime se jede?
- Zašto izmišljati još jedan kotač kada imamo Python, NodeJS, PHP, Go i druge super stvari s kojima su svi zadovoljni?
- I nekoliko primjera iz života. Reportažu sam morao dosta skraćivati jer mi je trebalo 3,5 sata pa će primjera biti malo.
OpenResty je NGINX. Zahvaljujući njemu, imamo potpuni web poslužitelj koji je dobro napisan i radi brzo. Mislim da većina nas koristi NGINX u proizvodnji. Svi znate da je brz i kul. Napravili su cool sinkroni I/O u njemu, tako da ne moramo ništa ciklirati, baš kao što su to učinili u Pythonu. Gevent je cool, super, ali ako napišete C kod i nešto krene krivo, onda ćete s Geventom poludjeti otklanjajući pogreške. Imao sam iskustvo: trebalo mi je puna dva dana da shvatim što je tu pošlo po zlu. Da netko nije kopao nekoliko tjedana, pronašao problem, napisao na Internetu, a Google ga nije pronašao, onda bismo totalno poludjeli.
NGINX već ima urađeno predmemoriranje i statički sadržaj. Ne trebate se brinuti kako to učiniti ljudski, da negdje ne usporite, da negdje ne izgubite deskriptore. Nginx je vrlo zgodan za implementaciju, ne morate razmišljati o tome što uzeti - WSGI, PHP-FPM, Gunicorn, Unicorn. Nginx je instaliran, dat adminima, oni znaju raditi s njim. Nginx obrađuje zahtjeve na strukturiran način. O ovome ću malo kasnije. Ukratko, ima fazu kada je samo prihvatio zahtjev, kada ga je obradio i kada je servirao sadržaj korisniku.
Nginx je cool, ali postoji jedan problem: nije dovoljno fleksibilan, čak ni sa svim cool značajkama koje su dečki ugurali u konfiguraciju, unatoč činjenici da se može konfigurirati. Ova snaga nije dovoljna. Zato su momci iz Taobaoa davno, čini se kao prije osam godina, ugradili Luu u njega. Što to daje?
- Veličina. Malo je. LuaJIT daje oko 100-200 kilobajta memorije i minimalno opterećenje performansi.
- Ubrzati. Interpretator LuaJIT blizak je C-u u mnogim situacijama, u nekim situacijama gubi od Jave, u drugima je nadmašuje. Neko se vrijeme smatrao vrhunskim, najcool JIT kompajlerom. Sada postoje hladniji, ali vrlo su teški, na primjer, isti V8. Neki JS tumači i Java HotSpot su brži u nekim točkama, ali na nekim mjestima ipak gube.
- Lako se uči. Ako imate, recimo, Perl kodnu bazu, a niste Booking, nećete naći Perl programere. Zato što ih nema, svi su odvedeni, a podučavanje je dugo i teško. Ako želite programere za nešto drugo, možda ćete ih morati prekvalificirati ili pronaći. U slučaju Lua, sve je jednostavno. Svaki junior može naučiti Lua za tri dana. Trebalo mi je oko dva sata da to shvatim. Dva sata kasnije već sam pisao kod u produkciji. Otprilike tjedan dana kasnije otišao je ravno u proizvodnju i otišao.
Kao rezultat, to izgleda ovako:

Ima puno toga ovdje. OpenResty je skupio hrpu modula, kako luash tako i onih enginea. I imate sve spremno - raspoređeno i radi.
Primjeri
Dosta tekstova, idemo na kod. Evo malo Hello Worlde:

Što je tamo? Ovo je lokacija Enginsa. Ne brinemo se, ne pišemo vlastito usmjeravanje, ne uzimamo neko gotovo - već ga imamo u NGINX-u, živimo dobar i lijen život.
content_by_lua_block je blok koji kaže da poslužujemo sadržaj pomoću Lua skripte. Uzimamo Engins varijablu remote_addr i stavi ga unutra string.format. Ovo je isto kao sprintf, samo u Lui, jedino ispravno. I dajemo ga klijentu.
Kao rezultat, izgledat će ovako:

No, vratimo se u stvarni svijet. Nitko ne implementira Hello World u proizvodnju. Naša aplikacija obično ide u bazu ili negdje drugdje i većinu vremena čeka odgovor.

Samo sjedi i čeka. Nije baš dobro. Kad dođe 100.000 korisnika, nama je jako teško. Pa upotrijebimo jednostavnu aplikaciju kao primjer. Tražit ćemo slike, na primjer, mačaka. Ali nećemo samo pretraživati, proširit ćemo ključne riječi i, ako je korisnik tražio "mačiće", pronaći ćemo mačke, dlakave mačke i tako dalje. Prvo, moramo dobiti podatke o zahtjevu na pozadini. Ovako izgleda:

Dvije linije vam omogućuju da odaberete GET parametre, bez komplikacija. Dalje, recimo, iz baze podataka sa znakom za ključnu riječ i ekstenzijom, dobivamo te informacije pomoću uobičajenog SQL upita. Jednostavno je. Ovako izgleda:

Povezivanje knjižnice resty.mysql, koje već imamo u kompletu. Ne trebamo ništa instalirati, sve je spremno. Navodimo kako se povezati i napraviti SQL upit:

Ovdje je malo zastrašujuće, ali sve radi. Ovdje je 10 granica. Izvučemo 10 unosa, lijeni smo, ne želimo pokazati više. Zaboravio sam na ograničenje u SQL-u.
Zatim nalazimo slike za sve upite. Skupljamo hrpu zahtjeva i ispunjavamo Lua tablicu tzv reqs, i mi to radimo ngx.location.capture_multi.

Svi ti zahtjevi se šalju paralelno, a odgovori nam se vraćaju. Vrijeme rada jednako je vremenu odziva najsporijeg. Ako svi snimimo u 50 milisekundi, a poslali smo stotinu zahtjeva, onda ćemo dobiti odgovor za 50 milisekundi.
Budući da smo lijeni i ne želimo pisati HTTP i rukovanje predmemorijom, natjerat ćemo NGINX da radi sve umjesto nas. Kao što ste vidjeli, postojao je zahtjev za url/fetch, Evo ga:

Mi to činimo jednostavnim proxy_pass, naznačimo gdje da predmemoriramo, kako to učiniti, i sve radi za nas.
Ali to nije dovoljno, još moramo dati podatke korisniku. Najjednostavnija ideja je serijalizirati sve u JSON-u, jednostavno, u dva retka. Dajemo Content-Type, dajemo JSON.
Ali postoji jedna poteškoća: korisnik ne želi čitati JSON. Moramo privući front-end programere. Ponekad to isprva ne želimo učiniti. A SEO stručnjaci će reći da ako mi tražimo slike, to im nije važno. A ako im damo neki sadržaj, reći će da naše tražilice ništa ne indeksiraju.
Što učiniti u vezi s tim? Naravno, korisniku ćemo dati HTML. Ručno generiranje nije comme il faut, pa želimo koristiti predloške. Za to postoji knjižnica lua-resty-template.

Vjerojatno ste vidjeli tri strašna slova OPM. OpenResty dolazi s vlastitim upraviteljem paketa, putem kojeg možete instalirati hrpu različitih modula, posebice, lua-resty-template. Ovo je jednostavan mehanizam za predloške, sličan Django predlošcima. Tamo možete napisati kod i izvršiti zamjenu varijabli.
Kao rezultat, sve će izgledati otprilike ovako:

Uzeli smo podatke i renderirali predložak, ponovno u dva retka. Korisnik je zadovoljan, dobio je mačke. Budući da smo proširili zahtjev, dobio je i medvjeda za mačiće. Nikad se ne zna, možda je upravo to tražio, ali nije mogao ispravno formulirati svoj zahtjev.
Sve je cool, ali mi smo u razvoju i još ne želimo to pokazati korisnicima. Napravimo autorizaciju. Da bismo to učinili, pogledajmo kako NGINX obrađuje zahtjev u uvjetima OpenRestyja:
- Prva faza - pristup, kada je korisnik tek došao, a mi smo ga gledali po zaglavljima, po IP adresi i po drugim podacima. Možemo ga odmah prekinuti ako nam se ne sviđa. To se može koristiti za autorizaciju ili ako primimo puno zahtjeva, možemo ih jednostavno prekinuti u ovoj fazi.
- prepisati. Prepisujemo neke podatke zahtjeva.
- sadržaj. Sadržaj dostavljamo korisniku.
- filter zaglavlja. Zamjenjujemo zaglavlja odgovora. Ako smo koristili
proxy_pass, možemo prepisati neka zaglavlja prije nego što ih damo korisniku. - filter tijela. Možemo promijeniti tijelo.
- klada — sječa. Možete pisati zapise u elasticsearch bez dodatnog sloja.
Naša će autorizacija izgledati otprilike ovako:

Ovo ćemo dodati onom location, koji smo prije opisali, i tamo stavite sljedeći kod:

Gledamo imamo li kolačić token. Ako ne, onda tražimo autorizaciju. Korisnici su lukavi i mogu pogoditi da trebaju postaviti kolačić token. Stoga ćemo ga također staviti u Redis:

Kod za rad s Redisom je vrlo jednostavan i ne razlikuje se od ostalih jezika. U isto vrijeme, svaki ulaz/izlaz, tu i tamo, ne blokira. Ako pišete sinkroni kod, on radi asinkrono. Skoro kao Gevent, ali dobro napravljeno.

Izvršimo samu autorizaciju:

Kažemo da trebamo pročitati tijelo zahtjeva. Primamo POST argumente i provjeravamo jesu li prijava i lozinka točni. Ako su netočni, izazivamo vas za autorizaciju. I ako je točno, napišite token u Redis:

Ne zaboravite postaviti kolačić, to se također radi u dva retka:

Primjer je jednostavan i spekulativan. Naravno, nećemo raditi servis koji ljudima prikazuje mačke. Ali tko nas zna. Dakle, prođimo kroz ono što se može učiniti u proizvodnji.
- Minimalistički backend. Ponekad moramo ispisati samo malo podataka u pozadinu: negdje moramo umetnuti datum, negdje moramo prikazati popis, reći koliko je korisnika trenutno na stranici, priložiti brojač ili statistiku. Nešto tako malo. Neki minimalni komadi mogu se napraviti vrlo jednostavno. To će ga učiniti brzim, lakim i izvrsnim.
- Predobrada podataka. Ponekad želimo ugraditi oglašavanje na našu stranicu, a primamo ga pomoću API zahtjeva. Ovdje je to vrlo lako učiniti. Ne opterećujemo naš backend, koji već sjedi i naporno radi. Možete ga preuzeti i preuzeti ovdje. Možemo spojiti neki JS ili, obrnuto, odvojiti ga i prethodno obraditi nešto prije nego to damo korisniku.
- Fasada za mikroservis. Ovo je također vrlo dobar slučaj, implementirao sam ga. Prije toga radio sam u Tenzoru, tvrtki koja se bavi elektroničkim izvješćivanjem i izvještava otprilike polovicu pravnih osoba u zemlji. Napravili smo uslugu, mnoge stvari su se tamo radile koristeći isti mehanizam: usmjeravanje, autorizacija i više.
OpenResty se može koristiti kao ljepilo za vaše mikroservise, pružajući jedinstven pristup svemu i jedno sučelje. Budući da se mikroservisi mogu napisati na takav način da imate Node.js ovdje, PHP ovdje, Python ovdje, nešto Erlang ovdje, razumijemo da ne želimo prepisivati isti kod posvuda. Stoga se OpenResty može priključiti na prednju stranu. - Statistika i analitika. Obično je NGINX na ulazu i svi zahtjevi idu preko njega. Na ovom je mjestu vrlo zgodno sakupljati. Možete odmah nešto izračunati i uploadati negdje, npr. Elasticsearch, Logstash ili jednostavno napisati u log pa poslati negdje.
- Višekorisnički sustavi. Na primjer, online igre su također vrlo dobre za izradu. Danas će u Cape Townu Alexander Gladysh govoriti o tome kako brzo izraditi prototip igre za više igrača koristeći OpenResty.
- Filtriranje zahtjeva (WAF). Danas je moderno izrađivati razne vrste vatrozida za web aplikacije; postoje mnoge usluge koje ih pružaju. Koristeći OpenResty možete sami napraviti vatrozid web aplikacije koji će jednostavno i lako filtrirati zahtjeve prema vašim zahtjevima. Ako imate Python, onda znate da PHP sigurno neće biti ubrizgan u vas, osim ako ga, naravno, ne stvorite bilo gdje iz konzole. Znate da imate MySQL i Python. Vjerojatno bi mogli pokušati napraviti neku vrstu obilaska direktorija i ubaciti nešto u bazu podataka. Stoga možete brzo i jeftino filtrirati čudne upite na početku.
- Zajednica. Budući da je OpenResty izgrađen na NGINX-u, ima bonus - ovo NGINX zajednica. Vrlo je velik, a pristojan dio pitanja koja ćete imati na početku već je riješila NGINX zajednica.
Lua programeri. Jučer sam razgovarao s dečkima koji su došli na HighLoad++ trening dan i čuo da je samo Tarantool napisan na Lua. Ovo nije istina, puno toga je napisano u Lui. Primjeri: OpenResty, Prosody XMPP server, Love2D game engine, Lua skriptiran u Warcraftu i drugdje. Postoji mnogo Lua programera, imaju veliku i responzivnu zajednicu. Sva moja pitanja o Lua-i riješena su u roku od nekoliko sati. Kad pišete na mailing listu, doslovno u roku od nekoliko minuta već postoji hrpa odgovora, koji opisuju što i kako, što je što. super je Nažalost, takva ljubazna, duhovna zajednica nije svugdje.
Postoji GitHub za OpenResty, gdje možete otvoriti problem ako je nešto pokvareno. Postoji lista za slanje e-pošte na Google grupama, gdje možete raspravljati o općim pitanjima, postoji lista za slanje e-pošte na kineskom - nikad se ne zna, možda ne govorite engleski, ali znate kineski.
Rezultati
- Nadam se da sam uspio prenijeti da je OpenResty vrlo zgodan okvir prilagođen webu.
- Ima niske barijere za ulazak, budući da je kod sličan onome u kojem pišemo, jezik je prilično jednostavan i minimalistički.
- Omogućuje asinkroni I/O bez povratnih poziva, nećemo imati nikakve rezance kao što ponekad možemo napisati u NodeJS.
- Ima jednostavnu implementaciju, budući da nam treba samo NGINX s potrebnim modulom i našim kodom, i sve radi odmah.
- Velika i osjetljiva zajednica.
Nisam detaljno ispričao kako se radi rutiranje, pokazalo se da je to jako duga priča.
Hvala vam!

Izvor: www.habr.com
