Vi publicerar Ă„terigen en transkription av föredraget frĂ„n konferensen 2016, som hölls i Skolkovo i Moskva-regionen den 7â8 november förra Ă„ret. berĂ€ttar hur man utökar funktionaliteten av NGINX med OpenResty och Lua.
Hej alla, jag heter Vladimir Protasov, jag arbetar pÄ Parallels. Jag ska berÀtta lite om mig sjÀlv. I tre fjÀrdedelar av mitt liv har jag Àgnat mig Ät att skriva kod. Jag blev programmerare Ànda in i sjÀlen, bokstavligen: ibland drömmer jag om kod. En fjÀrdedel av livet handlar om industriell utveckling, att skriva kod som gÄr direkt till produktion. Koden som vissa av er anvÀnder, men inte misstÀnker.
För att ni ska förstÄ hur illa det var. NÀr jag var en liten junior kom jag in och fick sÄdana tvÄterabyte databaser. Nu har alla highload hÀr. Jag gick pÄ konferenser och frÄgade: "Tja, berÀtta, har ni big data, Àr allt coolt? Hur stora databaser har ni?" De svarade: "Vi har 100 gigabyte!" Jag sa: "Coolt, 100 gigabyte!" Men tÀnkte för mig sjÀlv, hur jag skulle hÄlla en pokerface. Du tÀnker, ja, de Àr coola, och sen kommer du tillbaka och grÀver med dessa multiterabyte databaser. Och detta - nÀr jag var junior. Kan ni förestÀlla er hur hÄrt det var?
Jag kan mer Àn 20 programmeringssprÄk. Det Àr nÄgot jag har fÄtt lÀra mig under arbetets gÄng. Du fÄr kod pÄ Erlang, C, C++, Lua, Python, Ruby och nÄgot annat, och du mÄste arbeta med allt det. SÄ det blev sÄ. Jag har inte lyckats rÀkna det exakta antalet, men det förlorade jag mig nÄgonstans kring 20.
Eftersom alla nÀrvarande vet vad Parallels Àr och vad vi gör, kommer jag inte att prata om hur coola vi Àr och vad vi gör. Jag berÀttar bara att vi har 13 kontor runt om i vÀrlden, över 300 anstÀllda, utveckling i Moskva, Tallinn och Malta. Om sÄ önskas kan man faktiskt flytta till Malta om det Àr kallt pÄ vintern och man behöver vÀrma ryggen.
Specifikt vÄr avdelning skriver i Python 2. Vi sysslar med affÀrer och har inte tid att införa trendiga teknologier, sÄ vi lider. Vi har Django, för i den finns allt, och det överflödiga har vi tagit bort. Dessutom MySQL, Redis och NGINX. Vi har ocksÄ mÄnga andra coola saker. Vi har MongoDB, vÄra kaniner springer omkring, vi har sÄ mycket olika grejer - men det Àr inte mitt omrÄde, och jag sysslar inte med det.
OpenResty
Jag har berÀttat om mig sjÀlv. LÄt oss ta reda pÄ vad jag ska prata om idag:
- Vad Àr OpenResty och hur anvÀnds det?
- Varför uppfinna Ànnu en cykel nÀr vi har Python, NodeJS, PHP, Go och andra fantastiska saker som alla Àr nöjda med?
- Och lite exempel frÄn verkligheten. Jag var tvungen att kraftigt korta ned min presentation eftersom den blev 3,5 timmar lÄng, sÄ det blir fÄ exempel.
OpenResty Àr NGINX. Tack vare det har vi en fullt fungerande webbserver som Àr vÀlskriven och snabb. Jag tror att de flesta av oss anvÀnder NGINX i produktion. Alla vet att den Àr snabb och bra. Den har en fantastisk synkron in-/utmatning, sÄ vi behöver inte hitta pÄ nÄgot nytt som vi gjorde i Python med gevent. Gevent Àr fantastiskt, men om du skriver C-kod och nÄgot gÄr fel, kommer du att bli galen av att debugga det med gevent. Jag hade den erfarenheten: det tog mig tvÄ hela dagar att förstÄ vad som gÄtt fel. Om ingen hade grÀvt i det i flera veckor, hittat problemet, skrivit om det pÄ internet, och Google inte hade funnit det, skulle vi ha blivit helt galna.
I NGINX finns redan caching och statiskt innehĂ„ll. Du behöver inte oroa dig för hur man gör det pĂ„ ett korrekt sĂ€tt sĂ„ att det inte saktar ner nĂ„gonstans, eller att du tappar nĂ„gra deskriptorer. NGINX Ă€r vĂ€ldigt enkelt att distribuera, du behöver inte tĂ€nka pĂ„ vad du ska vĂ€lja â WSGI, PHP-FPM, Gunicorn, Unicorn. NGINX installeras, adminerna tar hand om det, och de vet hur man arbetar med det. NGINX strukturerat hanterar förfrĂ„gningar. Jag kommer att berĂ€tta lite mer om det senare. Kort sagt, det finns en fas nĂ€r den just har tagit emot förfrĂ„gan, nĂ€r den har behandlat den och nĂ€r den har returnerat innehĂ„llet till anvĂ€ndaren.
NGINX Àr bra, men det finns ett problem: det Àr inte tillrÀckligt flexibelt, Àven med alla de fantastiska funktionerna som utvecklarna har lagt in i konfigurationen. Denna kraft rÀcker inte till. DÀrför integrerade teamet frÄn Taobao för lÀnge sedan, verkar det vara för Ätta Är sedan, Lua i det. Vad ger det?
- Dimensions. Det Àr litet. LuaJIT ger ungefÀr 100-200 kilobyte overhead i minnet och minimal overhead i prestanda.
- Hastighet. LuaJIT-tolkaren Àr i mÄnga situationer nÀra C, i vissa situationer slÄr det Java, i andra Àr det snabbare Àn den. Under en tid ansÄgs det vara den mest avancerade JIT-kompilatorn. Numera finns det mer avancerade, men de Àr mycket tyngre, som till exempel V8. Vissa JS-tolkare och Java HotSpot Àr snabbare i vissa aspekter, men i andra omrÄden förlorar de fortfarande.
- AnvÀndarvÀnlighet. Om du till exempel har en kodbas i Perl, och du inte Àr Booking, kommer du inte att hitta Perl-programmerare. För de finns inte, de har alla blivit upptagna, och att lÀra dem tar tid och Àr svÄrt. Om du vill ha programmerare i nÄgot annat sprÄk, kanske du ocksÄ behöver omskola dem, eller sÄ fÄr du leta. För Lua Àr det enkelt. Lua kan lÀras av vilken junior som helst pÄ tre dagar. Jag behövde ungefÀr tvÄ timmar för att förstÄ det. Efter tvÄ timmar skrev jag redan kod i produktionen. UngefÀr en vecka senare var den direkt i produktionen.
Resultatet ser ut sÄ hÀr:

Det finns mycket hĂ€r. I OpenResty har de samlat en massa moduler, bĂ„de Lua och NGINX-specifika. Och allt Ă€r klart â bara dyk in och det fungerar.
Exempel
Nog med poesi, let's gÄ till koden. HÀr Àr en liten Hello World:

Vad finns hĂ€r? Det Ă€r en NGINX-location. Vi grĂ€mer oss inte, vi skriver ingen egen routing, vi tar ingen fĂ€rdig â vi har redan det i NGINX, vi lever bra och lat.
content_by_lua_block â det Ă€r en block som sĂ€ger att vi skickar innehĂ„ll med hjĂ€lp av ett Lua-skript. Vi tar NGINX-variabeln remote_addr och stoppar den i string.format. Det Ă€r samma sak som sprintf, bara pĂ„ Lua, bara korrekt. Och vi skickar det till kunden.
Resultatet ser ut sÄ hÀr:

Men lÄt oss ÄtervÀnda till verkligheten. Ingen deployar Hello World i produktion. VÄr applikation gör vanligtvis anrop till databasen eller nÄgon annanstans och större delen av tiden vÀntar pÄ svar.

Den sitter bara och vÀntar. Det Àr inte sÄ bra. NÀr 100 000 anvÀndare strömmar in, blir det mycket tufft för oss. SÄ lÄt oss ta fram ett enkelt exempel pÄ en applikation. Vi ska leta efter bilder, till exempel katter. Men vi ska inte bara leta, vi kommer att bredda sökord och om en anvÀndare söker pÄ "kattungar", sÄ ska vi hitta katter, fluffiga, och sÄ vidare. Först mÄste vi fÄ sökdata pÄ backend. Det ser ut sÄ hÀr:

TvÄ rader tillÄter dig att hÀmta GET-parametrar, inget krÄngel. DÀrefter fÄr vi informationen frÄn databasen med en table pÄ sökordet och filendelse med en vanlig SQL-frÄga. Det Àr enkelt. Det ser ut sÄ hÀr:

Vi kopplar in biblioteket resty.mysql, som vi redan har i paketet. Vi behöver inget installera, allt Àr klart. Vi anger hur vi ansluter och gör en SQL-frÄga:

Det Ă€r lite lĂ€skigt hĂ€r, men allt fungerar. HĂ€r Ă€r 10 â det Ă€r grĂ€nsen. Vi hĂ€mtar 10 poster, vi Ă€r lata och vill inte visa fler. Jag glömde att nĂ€mna grĂ€nsen i SQL.
Sedan hittar vi bilder för alla förfrÄgningar. Vi samlar en hög av förfrÄgningar och fyller en Lua-tabell som kallas reqs, och gör ngx.location.capture_multi.

Alla dessa förfrÄgningar gÄr parallellt, och vi fÄr tillbaka svar. Tiden för arbetet Àr lika med tiden för svaret frÄn den lÄngsammaste. Om alla svarar pÄ 50 millisekunder och vi skickade hundra förfrÄgningar, kommer svaret att komma tillbaka pÄ 50 millisekunder.
Eftersom vi Àr lata och inte vill skriva HTTP-hantering och caching, kommer vi att fÄ NGINX att göra allt Ät oss. Som ni sÄg, dÀr var en förfrÄgan till url/fetch, hÀr Àr den:

Vi gör en enkel proxy_pass, anger vart vi ska cacha, hur vi ska göra det, och allt fungerar.
Men det Àr inte tillrÀckligt, vi behöver ocksÄ ge data till anvÀndaren. Den enklaste idén Àr att serialisera allt till JSON, lÀtt, pÄ tvÄ rader. Vi ger Content-Type, vi ger JSON.
Men det finns en svÄrighet: anvÀndaren vill inte lÀsa JSON. Vi mÄste locka frontend-utvecklare. Ibland vill vi inte göra det frÄn början. SEO-experter kan sÀga att om vi letar efter bilder Àr det ointressant för dem. Men om vi ger dem nÄgot innehÄll, kommer de att sÀga att vÄra sökmotorer inte indexerar nÄgot.
Vad ska vi göra Ät det? SjÀlvklart kommer vi att ge HTML till anvÀndaren. Att generera det manuellt Àr inte bra, sÄ vi vill anvÀnda mallar. För detta finns det ett bibliotek lua-resty-template.

Ni har kanske sett de tre skrÀmmande bokstÀverna OPM. OpenResty kommer med sin egen pakethanterare, genom vilken man kan installera en massa olika moduler, bland annat lua-resty-template. Det Àr en enkel mallmotor, nÀra Django-mallar. DÀr kan man skriva kod och göra variabelsubstitution.
Resultatet kommer att se ungefÀr ut sÄ hÀr:

Vi tog data och renderade mallen igen i tvÄ rader. AnvÀndaren Àr glad, han fick kattbilder. Eftersom vi utvidgade förfrÄgan fick han ocksÄ en sjölejon. Vem vet, kanske han letade just efter det men inte kunde formulera sin förfrÄgan korrekt.
Det Àr fantastiskt, men vi Àr i utvecklingsskedet, och vi vill inte visa det för anvÀndarna Àn. LÄt oss göra autentisering. För att göra det, lÄt oss se hur NGINX hanterar en förfrÄgan i termer av OpenResty:
- Den första fasen â access, nĂ€r anvĂ€ndaren just har kommit in, och vi har tittat pĂ„ honom utifrĂ„n rubriker, IP-adress och annan information. Vi kan omedelbart stĂ€nga av honom om vi inte gillar det. Detta kan anvĂ€ndas för autentisering, eller om vi fĂ„r vĂ€ldigt mĂ„nga förfrĂ„gningar kan vi enkelt stĂ€nga av dem i detta skede.
- rewrite. Vi skriver om viss förfrÄgningsdata.
- innehÄll. Vi ger innehÄllet till anvÀndaren.
- rubriksfilter. Vi kan modifiera svarsrubriker. Om vi har anvÀnt
proxy_pass, kan vi skriva om vissa rubriker innan vi ger dem till anvÀndaren. - kroppsförfilter. Vi kan modifiera kroppen.
- log â loggning. Vi kan skriva loggar i elasticsearch utan ett extra lager.
VÄr autentisering kommer att se ut ungefÀr sÄ hÀr:

Vi lÀgger till detta i den plats, som vi beskrev tidigare, och sÀtter in följande kod:

Vi kollar om vi har en cookie-token. Om inte, omdirigerar vi till autentisering. AnvÀndarna Àr kloka och kan lista ut att de behöver sÀtta en cookie-token. DÀrför lÀgger vi Àven in den i Redis:

Koden för att arbeta med Redis Àr vÀldigt enkel och skiljer sig inte frÄn andra sprÄk. All input/output dÀr och hÀr Àr icke-blockerande. Om du skriver synkron kod fungerar det asynkront. UngefÀr som med gevent, fast gjort bra.

LÄt oss göra sjÀlva autentiseringen:

Vi sÀger att vi behöver lÀsa kroppsinnehÄllet för förfrÄgan. Vi fÄr POST-argument, kontrollerar att anvÀndarnamn och lösenord Àr korrekta. Om de Àr felaktiga, omdirigerar vi till autentisering. Men om de Àr korrekta, sparar vi token i Redis:

Glöm inte att sÀtta cookie, det görs ocksÄ pÄ tvÄ rader:

Ett enkelt, hypotetiskt exempel. Vi kommer sjÀlvklart inte att göra en tjÀnst som visar katter för mÀnniskor. Men vem vet. LÄt oss dÀrför titta pÄ vad som kan göras i produktion.
- Minimalistisk backend. Ibland behöver vi leverera en mycket liten mÀngd data till backend: ibland behöver vi sÀtta in ett datum, ibland visa en lista, berÀtta hur mÄnga anvÀndare som för nÀrvarande Àr pÄ webbplatsen, koppla in en rÀknare eller statistik. NÄgot litet. Minimala bitar kan göras vÀldigt lÀtt. Det kommer att bli snabbt, enkelt och bra.
- Förbehandling av data. Ibland vill vi integrera annonser pÄ vÄr sida, och vi hÀmtar dessa annonser via API-förfrÄgningar. Det Àr vÀldigt enkelt att göra just hÀr. Vi belastar inte vÄr backend, som redan arbetar hÄrt. Vi kan samla detta hÀr. Vi kan skapa lite JS eller, tvÀrtom, dela upp det, processa nÄgot innan vi ger det till anvÀndaren.
- Facad för mikrotjÀnst. Detta Àr ocksÄ ett mycket bra fall, jag har implementerat det. Tidigare arbetade jag pÄ företaget Tenzor, som sysslar med elektronisk rapportering och tillhandahÄller rapportering för ungefÀr hÀlften av företagen i landet. Vi gjorde en tjÀnst dÀr med hjÀlp av samma mekanism Àr mÄnga saker gjorda: ruttdelning, autentisering och annat.
OpenResty kan anvÀndas som lim för dina mikrotjÀnster, vilket ger en enhetlig Ätkomst till allt och ett enhetligt grÀnssnitt. Eftersom mikrotjÀnster kan skrivas sÄ hÀr, sÄ har du Node.js hÀr, PHP dÀr, Python hÀr, nÄgon grej pÄ Erlang, förstÄr vi att vi inte vill skriva samma kod överallt. SÄ OpenResty kan sÀttas pÄ fronten. - Statistik och analys. Vanligtvis stÄr NGINX vid ingÄngen, och alla förfrÄgningar gÄr genom honom. Just pÄ denna plats Àr det mycket bekvÀmt att samla. Man kan rÀkna nÄgot direkt och skicka det nÄgonstans, till exempel till Elasticsearch, Logstash eller helt enkelt logga det och sedan skicka det nÄgonstans.
- Flerspelarsystem. Till exempel Àr det ocksÄ mycket bra att göra online-spel. Idag i Kapstaden kommer Alexander Gladysh att berÀtta hur man snabbt prototyperar ett flerspelarspel med hjÀlp av OpenResty.
- Filtrering av förfrÄgningar (WAF). Nu Àr det populÀrt att göra alla möjliga web application firewalls, det finns mÄnga tjÀnster som tillhandahÄller dem. Med hjÀlp av OpenResty kan du skapa en web application firewall som enkelt och smidigt filtrerar förfrÄgningar enligt dina krav. Om du har Python förstÄr du att PHP inte kan injiceras, om du inte spawnar det nÄgonstans frÄn konsolen. Du vet att du har MySQL och Python. Kanske kan de försöka göra nÄgon typ av directory traversal och injicera nÄgot i databasen. DÀrför kan man snabbt och billigt filtrera bort misstÀnkt förfrÄgningar direkt pÄ fronten.
- Gemenskap. Eftersom OpenResty Àr byggt pÄ NGINX har det en bonus - det Àr NGINX-gemenskapDet Àr mycket stort, och en betydande del av de frÄgor som du kan ha i början har redan lösts av NGINX-communityt.
Lua-utvecklareI gÄr pratade jag med killarna som kom pÄ utbildningsdagen HighLoad++ och hörde att endast Tarantool Àr skrivet pÄ Lua. SÄ Àr det inte, det Àr mycket som Àr skrivet i Lua. Exempel: OpenResty, XMPP-servern Prosody, spelmotorn Love2D, Lua scriptas i Warcraft och pÄ andra stÀllen. Det finns mÄnga Lua-utvecklare, och deras community Àr stort och lyhört. Alla mina frÄgor om Lua blev lösta inom nÄgra timmar. NÀr man skriver till mailinglistan fÄr man bokstavligen svar inom nÄgra minuter, de förklarar vad och hur, vad som Àr vad. Det Àr riktigt bra. TyvÀrr Àr det inte alltid sÄ hÀr vÀnligt stÀmning i andra communitys.
Det finns en GitHub för OpenResty, dĂ€r kan man skapa en issue om nĂ„got gĂ„r sönder. Det finns en mailinglista i Google Groups dĂ€r man kan diskutera allmĂ€nna frĂ„gor, och det finns en lista pĂ„ kinesiska â kanske behĂ€rskar du inte engelska, men kan kinesiska.
Sammanfattning
- Jag hoppas att jag har kunnat förmedla att OpenResty Àr ett vÀldigt bekvÀmt ramverk anpassat för webben.
- Det har en lÄg intrÀdeströskel, eftersom koden liknar det vi skriver, sprÄket Àr ganska enkelt och minimalistiskt.
- Det erbjuder asynkron I/O utan callbacks, sÄ vi kommer inte att ha den spagetti-kod som vi ibland kan fÄ i NodeJS.
- Det har en enkel distribution, eftersom vi bara behöver NGINX med rÀtt modul och vÄr kod, sÄ fungerar allt direkt.
- Ett stort och lyhört community.
Jag gick inte in pÄ detaljer om hur routing görs, det skulle ha blivit en mycket lÄng berÀttelse.
Tack för uppmÀrksamheten!

KĂ€lla: habr.com
