Vi publiserer igjen transkripsjonen av foredraget fra konferansen 2016, som fant sted i Skolkovo utenfor Moskva 7-8. november i fjor. snakker om hvordan man kan utvide funksjonaliteten til NGINX ved hjelp av OpenResty og Lua.
Hei alle sammen, jeg heter Vladimir Protasov, og jeg jobber i Parallels. Jeg vil fortelle litt om meg selv. I tre fjerdedeler av livet mitt har jeg drevet med koding. Jeg ble programmerer til beinet, i ordets rette forstand: noen ganger drÞmmer jeg om kode. Den siste fjerdedelen av livet er industriell utvikling, skriving av kode som gÄr direkte i produksjon. Koden som noen av dere bruker, men som dere ikke er klar over.
For Ă„ gi dere et inntrykk av hvor dĂ„rlig det var. Da jeg var en ung junior, kom jeg inn i selskapet, og de ga meg sĂ„nne to terabyte databaser. Det er nĂ„ alle her har highload. Jeg var pĂ„ konferanser, spurte: «Hei, fortell meg, har dere big data, er det kult? Hvor mye har dere i databasene?» De svarte: «Vi har 100 gigabyte!» Jeg sa: «Kult, 100 gigabyte!» Men tenkte for meg selv, hvordan jeg skulle beholde pokerfjeset. Du tenker, ja, disse folkene er kule, og sĂ„ gĂ„r du tilbake og sysler med disse multi-terabyte databasene. Og dette varer â det er Ă„ vĂŠre junior. Kan dere forestille dere hvilket slag det er?
Jeg kan mer enn 20 programmeringssprÄk. Det er noe jeg mÄtte lÊre meg underveis i jobben. Du fÄr kode i Erlang, C, C++, Lua, Python, Ruby, og noe annet, og sÄ mÄ du bare jobbe med det. Det var nÞdvendig. Det eksakte antallet ble jeg ikke i stand til Ä telle, men jeg mistet tellingen etter 20.
Siden alle tilstedevÊrende vet hva Parallels er, og hva vi gjÞr, skal jeg ikke gÄ inn pÄ hvor kule vi er eller hva vi gjÞr. Jeg vil bare si at vi har 13 kontorer over hele verden, mer enn 300 ansatte, utvikling i Moskva, Tallinn, og pÄ Malta. Hvis Þnskelig kan du faktisk flytte til Malta hvis det er kaldt om vinteren og du trenger Ä varme ryggen.
VĂ„r avdeling skriver konkret i Python 2. Vi driver med virksomhet, og vi har ikke tid til Ă„ implementere trendy teknologier, sĂ„ vi lider. Vi bruker Django fordi den har alt, og vi kastet det unĂždvendige. OgsĂ„ MySQL, Redis, og NGINX. I tillegg har vi mange andre kule ting. Vi har MongoDB, vi har kaniner som lĂžper rundt, vi har sĂ„ mye rart â men det er ikke mitt ansvar, og jeg driver ikke med det.
OpenResty
Jeg har snakket om meg selv. La oss finne ut hva jeg skal snakke om i dag:
- Hva er OpenResty og hva kan man bruke det til?
- Hvorfor oppfinne et nytt hjul nÄr vi allerede har Python, NodeJS, PHP, Go og andre kule ting som alle er fornÞyde med?
- Og noen eksempler fra virkeligheten. Jeg mÄtte kutte kraftig ned pÄ presentasjonen min fordi den ble pÄ 3,5 timer, sÄ derfor vil det bli fÄ eksempler.
OpenResty er NGINX. Takket vÊre det har vi en fullt fungerende webserver som er godt skrevet og fungerer raskt. Jeg tror de fleste av oss bruker NGINX i produksjon. Alle vet at den er rask og fantastisk. Den har en imponerende synkron inn/-utdata, sÄ vi trenger ikke Ä finne opp kruttet pÄ nytt som i Python med gevent. Gevent er fantastisk, men hvis du skriver C-kode, og noe gÄr galt, kan du bli gal av Ä debugge det med gevent. Jeg har erfaring: det tok meg hele to dager Ä finne ut hva som gikk galt. Hvis noen ikke hadde gravd i det i flere uker, ikke funnet problemet, ikke skrevet om det pÄ nettet, og Google ikke hadde funnet det, hadde vi blitt sprÞ.
I NGINX er caching og statisk innhold allerede implementert. Du trenger ikke Ă„ bekymre deg for hvordan du kan gjĂžre dette pĂ„ en ordentlig mĂ„te, slik at det ikke henger seg opp noe sted, eller at du mister filbeskrivere. NGINX er veldig lett Ă„ deployere, du trenger ikke Ă„ tenke pĂ„ hva du skal bruke â WSGI, PHP-FPM, Gunicorn, Unicorn. Du installerer NGINX, gir det til administratorene, de vet hvordan de skal hĂ„ndtere det. NGINX behandler forespĂžrslene strukturert. Jeg skal si litt mer om dette senere. Kort sagt, den har en fase der den nettopp har mottatt forespĂžrselen, en fase der den har behandlet den, og en fase der den leverer innholdet til brukeren.
NGINX er flott, men det er ett problem: den er ikke fleksibel nok, selv med alle de kule funksjonene som folk har puttet inn i konfigurasjonen, til tross for at den kan konfigureres. Denne kraften er ikke tilstrekkelig. Derfor bygde folk fra Taobao for mange Är siden, omtrent Ätte Är siden, Lua inn i den. Hva gir det?
- StĂžrrelse. Den er liten. LuaJIT gir omtrent 100-200 kilobyte overhead i minne og minimal overhead i ytelse.
- Hastighet. LuaJIT-tolkeren er i mange situasjoner nÊrt C, i noen situasjoner taper den mot Java, og i andre overgÄr den den. En stund ble den ansett som state of the art, den mest imponerende JIT-kompilatoren. NÄ finnes det mer imponerende, men de er veldig tunge, som for eksempel V8. Noen JavaScript-tolker og Java HotSpot kan vÊre raskere i enkelte tilfeller, men i andre tilfeller taper de fortsatt.
- Enkel Ä lÊre. Hvis du har et kodebase i Perl, og du ikke er Booking, vil du ikke finne Perl-programmerere. Fordi de ikke finnes; de har blitt alle tatt, og det tar lang tid Ä lÊre dem. Hvis du vil ha programmerere pÄ noe annet, mÄ du kanskje ogsÄ lÊre dem pÄ nytt eller finne dem. NÄr det gjelder Lua, er det enkelt. Enhver nybegynner kan lÊre Lua pÄ tre dager. Jeg brukte omtrent to timer pÄ Ä forstÄ det. Etter to timer skrev jeg allerede kode i produksjon. Omtrent en uke senere var det allerede i produksjon.
Som et resultat ser det slik ut:

Det er mye her. I OpenResty har de samlet en haug med moduler, bĂ„de Lua-baserte og engine-moduler. Og alt er klart â jeg har deployert det, og det fungerer.
Eksempler
Nok med lyrikk, la oss gÄ til koden. Her er en liten Hello World:

Hva er her? Dette er en engine-lokasjon. Vi bryr oss ikke, vi skriver ikke vĂ„r egen routing, vi tar ikke noe klart â vi har allerede det i NGINX, vi lever godt og lat.
content_by_lua_block â dette er en blokk som sier at vi leverer innhold ved hjelp av et Lua-skript. Vi tar engine-variabelen remote_addr og setter den inn i string.format. Det er det samme som sprintf, bare pĂ„ Lua, bare riktig. Og vi gir det til klienten.
Som et resultat vil det se slik ut:

Men la oss gÄ tilbake til den virkelige verden. I produksjon deployerer ingen Hello World. Vanligvis gÄr applikasjonen vÄr til en database eller et annet sted, og store deler av tiden venter vi pÄ respons.

Den sitter bare og venter. Dette er ikke sÄ bra. NÄr 100.000 brukere kommer inn, er det veldig tungt for oss. SÄ la oss for eksempelets del lage en enkel applikasjon for Ä sÞke etter bilder, for eksempel katter. Men vi skal ikke bare sÞke som det; vi vil utvide nÞkkelordene, og hvis brukeren har sÞkt etter «kattunger», skal vi finne katter, fluffy osv. For Ä begynne med, mÄ vi hente forespÞrseldataene pÄ backend. Slik ser det ut:

To linjer lar deg hente GET-parametere, ingen problemer. Deretter fÄr vi informasjonen fra databasen med en tabell for nÞkkelord og utvidelser via en vanlig SQL-forespÞrsel. Alt er enkelt. Slik ser det ut:

Vi kobler til biblioteket resty.mysql, som vi allerede har i pakken. Vi trenger ikke Ă„ installere noe; alt er klart. Vi spesifiserer hvordan vi skal koble til og lager SQL-forespĂžrselen:

Det er litt skummelt her, men alt fungerer. Her er 10 - det er grensen. Vi henter 10 oppfĂžringer, vi er late og Ăžnsker ikke Ă„ vise mer. Jeg glemte Ă„ nevne grensen i SQL.
Videre finner vi bilder basert pÄ alle forespÞrslene. Vi samler en bunke forespÞrsel og fyller en Lua-tabell som heter reqs, og gjÞr ngx.location.capture_multi.

Alle disse forespÞrslene gÄr parallelt, og vi fÄr svar tilbake. Behandlingstiden er lik tiden til det langsomste svaret. Hvis vi fÄr alle svarene innen 50 millisekunder, og vi sendte ut hundre forespÞrsel, da kommer svaret vÄrt etter 50 millisekunder.
Siden vi er late og ikke Þnsker Ä skrive HTTP-behandling og caching, lar vi NGINX gjÞre alt dette for oss. Som du sÄ, var det en forespÞrsel til url/fetch, her er den:

Vi gjĂžr en enkel proxy_pass, angir hvor vi skal cache, hvordan vi skal gjĂžre det, og da fungerer alt.
Men dette er ikke nok, vi mÄ ogsÄ gi data til brukeren. Den enkleste ideen er Ä serialisere alt i JSON, enkelt, pÄ to linjer. Vi sender Content-Type og sender JSON.
Men det er en komplikasjon: brukeren Þnsker ikke Ä lese JSON. Vi mÄ tilkalle frontend-utviklere. Av og til vil vi ikke gjÞre dette i starten. Og SEO-ekspertene vil si at hvis vi leter etter bilder, er det dem uansett. Men hvis vi gir dem noe innhold, vil de si at sÞkemotorene ikke indekserer noe.
Hva skal vi gjĂžre med dette? SelvfĂžlgelig vil vi gi brukeren HTML. Ă
generere det manuelt er ikke bra, sÄ vi vil bruke maler. For dette har vi biblioteket lua-resty-template.

Du sÄ kanskje de tre skumle bokstavene OPM. OpenResty kommer med sin egen pakkebehandler, gjennom hvilken du kan installere en rekke forskjellige moduler, spesielt lua-resty-template. Dette er en enkel malmotor, nÊrt relatert til Django-maler. Der kan du skrive kode og gjÞre variabel-substitusjon.
Som et resultat vil alt se omtrent slik ut:

Vi hentet data og rendrete malen i to linjer. Brukeren er glad, han fikk katter. Siden vi utvidet forespÞrselen, fikk han ogsÄ en sjÞlÞve i tillegg til kattungene. Kanskje han egentlig lette etter den, men kunne ikke formulere forespÞrselen sin riktig.
Alt er flott, men vi er jo i utviklingen, og Þnsker ikke Ä vise dette til brukerne ennÄ. La oss sette opp autentisering. For Ä gjÞre dette, la oss se hvordan NGINX behandler forespÞrselen i forhold til OpenResty:
- Den fÞrste fasen - access, nÄr brukeren nettopp har kommet, og vi ser pÄ ham gjennom overskriftene, IP-adressen, og annen informasjon. Vi kan umiddelbart kutte ham av hvis vi ikke liker ham. Dette kan brukes til autentisering, eller hvis vi fÄr for mange forespÞrsel, kan vi enkelt kutte dem i denne fasen.
- gjen skrive. Vi gjen skriver noen forespĂžrsel data.
- content. Vi gir innholdet til brukeren.
- overskrifter filter. Vi kan endre overskriftene i svaret. Hvis vi har brukt
proxy_pass, kan vi omskrive noen overskrifter fĂžr vi gir dem til brukeren. - kropp filter. Vi kan endre kroppen.
- log â logging. Vi kan skrive logger til elasticsearch uten et ekstra lag.
VÄr autentisering vil se omtrent slik ut:

Vi legger dette til den plassering, som vi beskrev tidligere, og putter inn fĂžlgende kode:

Vi sjekker om vi har en cookie token. Hvis ikke, sender vi til autentisering. Brukerne er smarte og kan gjette at de mÄ sette en cookie token. Derfor legger vi den ogsÄ i Redis:

Koden for Ä arbeide med Redis er veldig enkel og skiller seg ikke fra andre sprÄk. I tillegg er all input/output der, som her, ikke blokkering. Hvis du skriver synkron kode, fungerer det asynkront. Omtrent som med gevent, men det er gjort bedre.

La oss lage selve autentiseringen:

Vi sier at vi trenger Ä lese kroppen i forespÞrselen. Vi fÄr POST-argumentene, sjekker om brukernavnet og passordet er riktige. Hvis de ikke er riktige, sender vi til autentisering. Og hvis de er riktige, registrerer vi token i Redis:

Ikke glem Ä sette cookie, dette gjÞres ogsÄ i to linjer:

Et enkelt, hypotetisk eksempel. Vi kommer selvfÞlgelig ikke til Ä lage en tjeneste som viser folk katter. Men hvem vet. SÄ la oss se pÄ hva vi kan gjÞre i produksjon.
- Minimalistisk backend. Av og til trenger vi Ä gi backend bare litt data: noen ganger mÄ vi sette inn en dato, vise en liste, si hvor mange brukere som er pÄ nettstedet, legge til en teller eller statistikk. Noe lite som det. Minimumsdelene kan lager veldig enkelt. Dette vil gÄ raskt, lett og bra.
- Forbehandling av data. Noen ganger Þnsker vi Ä integrere annonser pÄ siden vÄr, og vi henter disse annonsene via API-forespÞrslene. Dette er veldig enkelt Ä gjÞre her. Vi laster ikke ned serveren vÄr, som allerede jobber hardt. Vi kan ta tak i og samle alt her. Vi kan lage noen JS-moduler eller, omvendt, splitte opp og preprocessere noe fÞr vi leverer det til brukeren.
- Grensesnitt for mikrotjenester. Dette er ogsÄ et veldig godt case, jeg har implementert det. FÞr dette jobbet jeg i selskapet Tenzor, som driver med elektronisk rapportering og tar seg av rapporteringen for omtrent halvparten av juridiske enheter i landet. Vi lagde en tjeneste hvor mange funksjoner ble realisert med samme mekanisme: ruting, autorisasjon og mer.
OpenResty kan brukes som lim for mikrotjenestene dine, noe som gir enhetlig tilgang til alt og et enhetlig grensesnitt. Siden mikrotjenester kan vÊre skrevet i forskjellige sprÄk som Node.js, PHP, Python, og noe som kjÞrer pÄ Erlang, forstÄr vi at vi ikke Þnsker Ä skrive den samme koden overalt. Derfor kan OpenResty settes opp pÄ frontenden. - Statistikk og analyse. Vanligvis stÄr NGINX foran, og alle forespÞrslene gÄr gjennom det. Her er det veldig praktisk Ä samle data. Man kan umiddelbart utfÞre beregninger og sende dem et sted, for eksempel til Elasticsearch, Logstash eller bare skrive dem til logg for senere sending.
- Flerebrukersystemer. For eksempel er det ogsÄ veldig bra Ä lage online-spill. I dag vil Alexander Gladys i Cape Town snakke om hvordan man raskt kan prototype et flerebrukerspill ved hjelp av OpenResty.
- ForespÞrsel filtrering (WAF). NÄ er det populÊrt Ä lage diverse webapplikasjonsbrannmurer, og det finnes mange tjenester som tilbyr dem. Med OpenResty kan man lage sin egen webapplikasjonsbrannmur som enkelt og greit kan filtrere forespÞrslene etter dine krav. Hvis du jobber med Python, vet du at PHP antageligvis ikke kan injiseres, med mindre du spawner det fra konsollen. Du vet at du har MySQL og Python, sÄ kanskje noen prÞver Ä utfÞre directory traversal og injisere noe i databasen. Derfor kan man raskt og kostnadseffektivt filtrere uÞnskede forespÞrslene pÄ frontenden.
- Fellesskapet. Siden OpenResty er bygget pÄ NGINX, har det en bonus - det er NGINX-fellesskapetDet er veldig stort, og en betydelig del av spÞrsmÄlene du mÄtte ha i starten, er allerede lÞst av NGINX-samfunnet.
Lua-utviklereI gÄr snakket jeg med folk som kom til HighLoad++ lÊringsdagen og hÞrte at det kun er Tarantool skrevet i Lua. Det er ikke sant, det er mye skrevet i Lua. Eksempler: OpenResty, XMPP-serveren Prosody, spillmotoren Love2D, Lua skriptes i Warcraft og andre steder. Det er mange Lua-utviklere, og de har et stort og hjelpsomt fellesskap. Alle mine spÞrsmÄl om Lua ble lÞst i lÞpet av noen timer. NÄr man skriver til e-postlisten, fÄr man bokstavelig talt en haug med svar innen minutter, de forklarer hva og hvordan, hva som gjelder. Det er veldig flott. Dessverre er ikke alle slike vennlige sjeler.
Det finnes et GitHub for OpenResty, der kan man opprette en issue hvis noe er Þdelagt. Det finnes en e-postliste pÄ Google Groups hvor man kan diskutere generelle spÞrsmÄl, det er ogsÄ en mailingliste pÄ kinesisk - man vet aldri, kanskje man ikke behersker engelsk, men kan kinesisk.
Resultater
- Jeg hÄper jeg har klart Ä formidle at OpenResty er et veldig praktisk rammeverk, skreddersydd for web.
- Det har lav terskel for inntreden, siden koden ligner pÄ det vi skriver, sprÄket er ganske enkelt og minimalistisk.
- Det tilbyr asynkron I/O uten tilbakemeldinger, vi fÄr ikke spagetti som vi noen ganger kan skrive i NodeJS.
- Det har enkel distribusjon, siden vi kun trenger NGINX med det nÞdvendige modulen og koden vÄr, sÄ fungerer alt med en gang.
- Et stort og responsivt fellesskap.
Jeg forklarte ikke i detalj hvordan ruting gjĂžres, det ville blitt en veldig lang historie.
Takk for oppmerksomheten!

Kilde: habr.com
