Po publikojmë përsëri përmbledhjen e raportit nga konferenca 2016, që u mbajt në Skolkovo, rajoni i Moskës më 7—8 nëntor të vitit të kaluar. tregon si të zgjeroni funksionalitetin e NGINX me OpenResty dhe Lua.
Përshëndetje të gjithëve, emri im është Vladimir Protasov, punoj në Parallels. Do të flas pak për veten. Tre të katërtat e jetës time kam kaluar duke shkruar kod. Jam bërë programues në kuptimin e plotë të fjalës: ndonjëherë shoh kod në ëndrrat e mia. Një katërta e jetës është zhvillim industrial, shkruaj kod që shkon drejtpërdrejt në prodhim. Kodu që disa prej jush e përdorin, por nuk e dijnë këtë.
Që të kuptoni sa keq ishte gjithçka. Kur isha një junior i vogël, erdha, dhe më dhanë baza dy terabajt. Tani të gjithë këtu janë highload. Shkova në konferenca dhe pyeta: "Djem, tregoni, a keni big data, është super? Sa ka aty databaza juaj?" Më përgjigjën: "Ne kemi 100 gigabajt!" Unë thashë: "Super, 100 gigabajt!" Ndërsa brenda meje mendoja si të ruaja një pokerface. Mendon, po, djemtë janë të shkëlqyer, dhe pastaj kthehesh dhe shkruan me këto baza me shumë terabajt. Dhe kjo është kur isha junior. Mendoni për atë goditje!
Unë njoh mbi 20 gjuhë programimi. Kjo është ajo që duhej të kuptoja gjatë punës. Të japin kod në Erlang, në C, në C++, në Lua, në Python, në Ruby, në diçka tjetër, dhe duhet ta kodosh gjithçka. Në përgjithësi, duhej ta bënte. Sasia e saktë nuk arrita ta llogaris, por ndoshta numri humbi rreth 20.
Duke që të gjithë të pranishmit dinë se çfarë është Parallels dhe çfarë bëjmë ne, nuk do të flas për sa të shkëlqyer jemi dhe çfarë bëjmë. Do të përmend vetëm se kemi 13 zyra në botë, mbi 300 punonjës, zhvillim në Moskë, Talin dhe Mal të Zi. Nëse dëshiron, mund të marrësh dhe të transferohesh në Maltë nëse dimri është i ftohtë dhe duhet të ngrohim shpinën.
Saktësisht departamenti ynë shkruan në Python 2. Ne merremi me biznes dhe nuk kemi kohë të aplikojmë teknologji të reja, prandaj vuajmë. Ne përdorim Django, sepse ka gjithçka që na nevojitet, dhe heqim atë që është e tepërt. Po ashtu, MySQL, Redis dhe NGINX. Kemi shumë gjëra të tjera interesante. Kemi MongoDB, kemi lepuj që vrapojnë, kemi gjithçka - por kjo nuk është për mua dhe nuk merrem me të.
OpenResty
Flas për veten. Le të shohim për çfarë do të flas sot:
- Çfarë është OpenResty dhe si përdoret?
- Pse të shpikim një tjetër biçikletë kur kemi Python, NodeJS, PHP, Go dhe teknologji të tjera të shkëlqyera, me të cilat të gjithë janë të kënaqur?
- Dhe disa shembuj nga jeta. Më është dashur të shkurtoj ndjeshëm prezantimin, sepse po dilte 3.5 orë, prandaj shembujt do të jenë të pakët.
OpenResty është NGINX. Falë tij, ne kemi një server të plotë web, i shkruar mirë dhe që funksionon shpejt. Mendoj se shumica prej nesh përdorin NGINX në prodhim. Të gjithë e dini se është i shpejtë dhe i mrekullueshëm. Ka një hyrje/dalje sinkrone të shkëlqyer, prandaj nuk na nevojitet të shpikim diçka siç bëjmë me gevent në Python. Gevent është i shkëlqyer, por nëse shkruani kod në C dhe diçka shkon keq, atëherë do të çmendeni duke e debugg-uar me gevent. Kam pasur përvojë: më nevojiteshin dy ditë për të kuptuar se çfarë kishte shkuar keq. Sikur dikush të mos kishte hulumtuar për disa javë, të mos kishte gjetur problemin, të mos kishte shkruar në internet dhe Google të mos kishte gjetur këtë, do të ishim çuar krejt në çmenduri.
Nginx tashme ka caching dhe përmbajtje statike. Nuk keni nevojë të shqetësoheni se si ta bëni siç duhet, që askund të mos keni pengesa dhe të mos humbni ndonjë descriptor. Deployimi i Nginx është shumë i lehtë, nuk keni nevojë të mendoni çfarë të merrni — WSGI, PHP-FPM, Gunicorn, Unicorn. E keni vendosur Nginx, ia keni dhënë administratorëve, ata dinë si ta përdorin këtë. Nginx e përpunon kërkesat në një mënyrë të strukturuar. Do flas pak më vonë për këtë. Në përmbledhje, ai ka një fazë, kur pranon kërkesën, kur e përpunon dhe kur i jep përmbajtjen përdoruesit.
Nginx është i shkëlqyer, por ka një problem: nuk është mjaft fleksibël, edhe me të gjitha ato karakteristika të mira që ata e shtuan në konfigurim, duke marrë parasysh që gjithçka mund të konfigurohet. Këto kapacitete janë të pamjaftueshme. Prandaj, ekipi i Taobao, ndoshta para tetë vitesh, integroi Lua. Çfarë ofron ky?
- Madhësia. Ai është i vogël. LuaJIT i jep rreth 100-200 kilobajt mbetje në kujtesë dhe një mbetje minimale në performancë.
- Shpejtësia. Ndërprerësi LuaJIT, në shumë situata, është afër performancës së C, në disa raste humbet ndaj Java dhe në disa e tejkalon atë. Një kohë të gjatë është konsideruar si state of the art, kompajleri JIT më i avancuar. Tani ka më të avancuar, por janë shumë të rëndë, si për shembull V8. Disa interpretuese JavaScript dhe HotSpot i Java-s janë më të shpejtë në disa pika, por ende humbasin në disa të tjera.
- Thjeshtësia në mësim. Nëse keni, për shembull, një bazë kodi në Perl dhe nuk jeni Booking, nuk do të gjeni programues Perl. Sepse nuk ka, të gjithë u morën, dhe të mësosh është e gjatë e e komplikuar. Nëse doni programues për diçka tjetër, ndoshta do t'ju duhet t'i riedukoni, ose t'i gjeni. Në rastin e Lua-s, gjithçka është e thjeshtë. Lua mësohet nga çdo junior për tri ditë. Më nevojiteshin rreth dy orë për të kuptuar. Pas dy orësh, isha duke shkruar kod për prodhim. Diku pas një javë, ai doli direkt në prodhim.
Si rezultat, kjo duket kështu:

Këtu ka shumë gjëra. Në OpenResty janë mbledhur shumë module, si të Lua-s, ashtu edhe të engine-s. Dhe gjithçka është gati — e ndërron dhe punon.
Shembuj
Mjaft me lirika, kalojmë te kodi. Ja një Hello World të vogël:

Çfarë ka këtu? Kjo është një lokacion i endjinseve. Ne nuk shqetësohemi, nuk shkruajmë rrugëtimin tonë, nuk marrim ndonjë të gatshëm — ne tashmë e kemi në NGINX, jetojmë mirë dhe me lenësi.
content_by_lua_block – është një bllok që thotë se ne po dorëzojmë përmbajtjen me ndihmën e skriptit Lua. Marrim një variabël endjinse remote_addr dhe e futim atë në string.format. Kjo është e njëjta gjë si sprintf, vetëm në Lua, vetëm e saktë. Dhe e dorëzojmë klientit.
Si rezultat, kjo do të duket kështu:

Por kthehemi në botën reale. Në prodhim, askush nuk e hedh Hello World. Aplikacioni ynë zakonisht shkon në bazë ose diku tjetër dhe një pjesë të madhe të kohës pret një përgjigje.

Thjesht ulur dhe duke pritur. Kjo nuk është shumë mirë. Kur vijnë 100,000 përdorues, na vjen shumë vështirë. Prandaj le të krijojmë një aplikacion të thjeshtë si shembull. Do të kërkojmë imazhe, për shembull, të maceve. Por ne nuk do të kërkojmë thjesht kështu, do të zgjerojmë fjalët kyçe dhe, nëse përdoruesi kërkon 'macet e vogla', ne do t'i gjejmë macet, të leshta dhe të tjera. Në fillim na nevojitet të marrim të dhënat e kërkesës në backend. Kjo duket kështu:

Dë dy rreshta ju lejojnë të marrim parametrat GET pa asnjë vështirësi. Më pas, supozojmë se marrim informacionin nga një databazë me një tabelë sipas fjalës kyçe dhe zgjerimit me një SQL query të zakonshme. Është e thjeshtë. Kjo duket kështu:

Këndojmë bibliotekën resty.mysql, e cila është tashmë në paketë. Nuk na nevojitet asgjë tjetër për tu instaluar, gjithçka është gati. Tregojmë si të lidhemi dhe bëjmë SQL query:

Këtu është pak e frikshme, por gjithçka funksionon. Numri 10 është kufiri. Ne nxjerrim 10 të dhëna, jemi të lodhur, nuk duam të tregojmë më shumë. Harrova për kufirin në SQL.
Më pas, ne të gjejmë imazhet për të gjitha kërkesat. Ne grumbullojmë një grup kërkesash dhe plotësojmë një tabelë Lua, e cila quhet reqs, dhe bëjmë ngx.location.capture_multi.

Të gjitha këto kërkesa shkojnë paralelisht, dhe ne marrim përgjigjet. Koha e ekzekutimit është e barabartë me kohën e përgjigjes më të ngadaltë. Nëse të gjitha përfundojnë për 50 milisekonda, dhe ne dërgojmë njëqind kërkesa, përgjigja do të vijë për 50 milisekonda.
Pasi jemi të lodhur dhe nuk duam të shkruajmë trajtimin e HTTP dhe caching, do ta bëjmë NGINX të bëjë gjithçka për ne. Siç e keni parë, ka pasur një kërkesë për url/fetch, kjo është:

Bëjmë një thjesht proxy_pass, tregojmë se ku të cache-ojmë, si ta bëjmë këtë, dhe gjithçka funksionon.
Por kjo nuk është e mjaftueshme, na duhet gjithashtu të kthejmë të dhënat te përdoruesi. Ideja më e thjeshtë është ta serializojmë gjithçka në JSON, lehtë, në dy rreshta. Kthejmë Content-Type, kthejmë JSON.
Por ka një vështirësi: përdoruesi nuk do të lexojë JSON-in. Duhet të angazhojmë frontend-erët. N sometimes kurrë nuk na pëlqen ta bëjmë këtë në fillim. Edhe seo specialistët do të thonë se nëse kërkojmë imazhe, atëherë ata nuk kanë rëndësi. Ndërsa nëse i japim ndonjë përmbajtje, ata do të thonë se motorët tanë të kërkimit nuk indeksojnë asgjë.
Çfarë të bëjmë me këtë? Natyrisht, ne do t’i dërgojmë përdoruesit HTML. Të gjenerojmë manualisht - nuk është profesionale, prandaj duam të përdorim shabllone. Për këtë ka një bibliotekë lua-resty-template.

Ju ndoshta keni parë tre shkronja të frikshme OPM. OpenResty vjen me menaxherin e tij të paketave, përmes të cilit mund të instaloni edhe shumë module të tjera, në veçanti, lua-resty-template. Ky është një motor i thjeshtë shabllonesh, i ngjashëm me shabllonet Django. Atje mund të shkruani kod dhe të bëni zëvendësimin e variablave.
Si rezultat, gjithçka do të duket pak a shumë kështu:

Kemi marrë të dhënat dhe e kemi renderuar modelin përsëri në dy rreshta. Përdoruesi është i lumtur, ka marrë macet. Për shkak se ne zgjatëm kërkesën, ai për macet e vogla mori edhe një uturak deti. Ndoshta ai po e kërkonte atë, por nuk mund t'i formulonte saktë kërkesat e tij.
E gjitha është e mrekullueshme, por ne jemi në zhvillim dhe nuk duam për momentin t'u tregojmë përdoruesve. Le të bëjmë autorizimin. Për ta bërë këtë, le të shohim si NGINX përpunon kërkesën në kushtet e OpenResty:
- Faza e parë — access, kur përdoruesi sapo erdhi, dhe ne e shqyrtuam atë përmes titujve, adresës IP, dhe të dhënave të tjera. Mund ta ndalim menjëherë, nëse nuk na pëlqen. Kjo mund të përdoret për autorizim, ose, nëse na vijnë shumë kërkesa, ne mund t'i ndalim lehtësisht në këtë fazë.
- rewrite. Rwriting disa të dhëna të kërkesës.
- content. I ofrojmë përmbajtjen përdoruesit.
- headers filter. Ndryshojmë titujt e përgjigjes. Nëse kemi përdorur
proxy_pass, mund të riformulojmë disa tituj, para se t'i dorëzojmë përdoruesit. - body filter. Mund të ndërlikojmë trupin.
- log — regjistrimi. Mund të shkruani regjistrat në elasticsearch pa një shtesë të ardhshme.
Autentifikimi ynë do të duket më pak si kjo:

Do ta shtojmë këtë në lokacionin, që përshkruam më parë, dhe do të fusim atje një kod të tillë:

Po shikojmë nëse kemi cookie token. Nëse jo, atëherë e drejtojmë në autentifikim. Përdoruesit janë të zgjuar dhe mund të supozojnë se duhet të vendosin cookie token. Prandaj, do ta ruajmë edhe në Redis:

Kodi për të punuar me Redis është shumë i thjeshtë dhe nuk ndryshon nga gjuhët e tjera. Në të njëjtën kohë, të gjithë input/output atje dhe këtu janë jo-bllokues. Nëse shkruani kod sinhron, funksionon asinkron. Diku si me gevent, vetëm se është realizuar mirë.

Le të bëjmë autentifikimin vetë:

Them se na nevojitet të lexojmë trupin e kërkesës. Marrim argumentet POST, kontrollojmë që emri i përdoruesit dhe fjalëkalimi janë të saktë. Nëse janë të gabuara, e drejtojmë në autentifikim. Por nëse janë të sakta, regjistrojmë token në Redis:

Mos harro të vendosësh cookie, kjo gjithashtu bëhet me dy rreshta:

Shembulli është i thjeshtë, hipotetik. Sigurisht që nuk do të bëjmë një shërbim që u tregon njerëzve mace. Megjithatë, kush e di. Prandaj, le të kalojmë për atë që mund të bëjmë në prodhim.
- Backend minimalist. Ndonjëherë na nevojitet të ofrojmë vetëm një sasi të vogël të të dhënave në backend: ndonjëherë duhet të shtojmë një datë, ndonjëherë të shfaqim një listë, të tregojmë se sa përdorues ka aktualisht në faqe, të lidhen një numërues ose statistikë. Diçka e tillë e vogël. Këto copëza minimale mund të bëhen shumë lehtësisht. Kështu do ta bëjmë shpejt, lehtësisht dhe shkëlqyeshëm.
- Parapërpunimi i të dhënave. Ndonjëherë dëshirojmë të integrojmë reklama në faqen tonë, dhe këto reklama i marrim përmes kërkesave API. Kjo është shumë lehtë të realizohet pikërisht këtu. Nuk e ngarkojmë backend-in tonë, i cili tashmë është duke punuar me vështirësi. Mund të marrim dhe të nxjerrim diçka këtu. Mund të krijojmë disa JS ose, përkundrazi, t'i ndarim ato, të kryejmë ndonjë parapërpunim para se t'ia dorëzojmë përdoruesit.
- Facada për mikroshërbimin. Ky është gjithashtu një rast shumë i mirë, që e kam realizuar. Para kësaj kam punuar në kompaninë Tenzor, e cila merret me raportimin elektronik dhe siguron raportimin për rreth gjysmën e personave juridikë në vend. Ne krijuam një shërbim, ku me ndihmën e këtij mekanizmi u bënë shumë gjëra: rrugëzimi, autorizimi dhe të tjera.
OpenResty mund të përdoret si ngjitës për mikroshërbimet tuaja, duke ofruar një qasje të unifikuar dhe një ndërfaqe të vetme. Pasi mikroshërbimet mund të jenë të shkruara në forma të ndryshme, si për shembull, këtu keni Node.js, aty keni PHP, këtu Python, dhe ndonjë gjë tjetër në Erlang, ne kuptojmë që nuk duam të shkruajmë të njëjtin kod kudo. Prandaj, OpenResty mund të vendoset në front. - Statistika dhe Analitika. Normalisht NGINX është në hyrje, dhe të gjitha kërkesat kalojnë përmes tij. Pikërisht në këtë pikë është shumë e lehtë të mblidhen të dhënat. Mund të bëni ndonjë llogaritje dhe ta dërgoni diku, për shembull në Elasticsearch, Logstash, ose thjesht ta regjistroni në log dhe pastaj ta dërgoni diku tjetër.
- Sistemet shumëpërdorues. Për shembull, lojërat online gjithashtu bëhen shumë mirë. Sot në Cape Town, Aleksandër Gladys do të flasë se si të prototipizoni shpejt një lojë shumëpërdorueshëm duke përdorur OpenResty.
- Filtrimi i kërkesave (WAF). Tani kohë është në modë të bëhet një web application firewall, ka shumë shërbime që i ofrojnë ato. Me OpenResty, mund të krijoni një web application firewall, i cili do të filtrojë lehtësisht kërkesat sipas kërkesave tuaja. Nëse keni Python, e kuptoni se PHP nuk do të injektohet me siguri, përveç nëse, natyrisht, e shpërtheni nga konsola kudo. E dini që keni MySQL dhe Python. Mbase këtu dikush mund të përpiqet të bëjë ndonjë directory traversal dhe të injektojë diçka në bazë. Prandaj, mund të filtrohen kërkesat shqetësuese shpejt dhe me çmim të ulët menjëherë në front.
- Komuniteti. Duke qenë se OpenResty është ndërtuar mbi NGINX, ai ka një bonus - është komuniteti NGINX. Ai është shumë i madh, dhe një pjesë e konsiderueshme e pyetjeve që do të keni në fillim është zgjidhur tashmë nga komuniteti NGINX.
Zhvilluesit e Lua. Dje e bisedova me djemtë që erdhën në ditën e trajnimit HighLoad++ dhe dëgjova se vetëm Tarantool është shkruar në Lua. Kjo nuk është e vërtetë; shumë gjëra janë shkruar në Lua. Disa shembuj përfshijnë: OpenResty, serverin XMPP Prosody, motorin e lojës Love2D, Lua përdoret në Warcraft dhe në vende të tjera. Ka shumë zhvillues Lua, dhe ata kanë një komunitet të madh e përgjegjës. Të gjitha pyetjet e mia për Lua u zgjidhen brenda disa orësh. Kur shkruan në listën e postimeve, gati për disa minuta merr një mori përgjigjesh, duke shpjeguar çdo gjë. Kjo është shumë e bukur. Fatkeqësisht, nuk është gjithmonë një komunitet kaq i dashur.
Për OpenResty ka një GitHub, ku mund të hapni një çështje nëse ndodhi ndonjë problem. Ka një listë postimesh në Google Groups, ku mund të diskutoni pyetje të përgjithshme, ka një listë postimesh në kinezisht — ndoshta nuk e zotëroni anglishten, por keni njohuri në kinezisht.
Përfundimet
- Shpresoj se kam arritur të shpreh se OpenResty është një framework shumë i përshtatshëm, i orientuar për web.
- Ai ka një prag të ulët hyrjeje, pasi kodi ngjan me atë që ne shkruajmë, gjuha është e thjeshtë dhe minimaliste.
- Ai ofron I/O asinkron pa callback-e, kështu që nuk do kemi një kod të komplikuar, ashtu si mund të shkruajmë ndonjëherë në NodeJS.
- Ka për të deponim të lehtë, pasi na nevojitet vetëm NGINX me modulin përkatës dhe kodi ynë, dhe gjithçka funksionon menjëherë.
- Një komunitet i madh dhe reagues.
Nuk e kam përmendur në detaje si bëhet routing, do të dilte një histori shumë e gjatë.
Faleminderit për vëmendjen!

Burimi: habr.com
