OpenResty: trasformiamo NGINX in un server applicativo completo

OpenResty: trasformiamo NGINX in un server applicativo completoPubbliciamo nuovamente la trascrizione della relazione della conferenza HighLoad++ 2016, che si è tenuta a Skolkovo, nella regione di Mosca, dal 7 all'8 novembre dello scorso anno. Vladimir Protasov spiega come espandere le funzionalità di NGINX con OpenResty e Lua.

Ciao a tutti, sono Vladimir Protasov, lavoro in Parallels. Racconterò un po' di me. Per tre quarti della mia vita mi occupo di scrivere codice. Sono diventato programmatore fino al midollo, nel senso letterale: a volte sogno di vedere codice. Un quarto della mia vita è dedicato allo sviluppo industriale, scrivendo codice che va direttamente in produzione. È il codice che alcuni di voi utilizzano, ma di cui non si rendono conto.

Per farvi capire quanto era difficile. Quando ero un giovane junior, sono arrivato e mi hanno dato queste basi da due terabyte. Ora qui tutti hanno highload. Andavo a conferenze, chiedendo: «Ragazzi, raccontateci, avete big data, tutto fantastico? Quanti dati avete?» Mi rispondevano: «Abbiamo 100 gigabyte!» E io dicevo: «Fantastico, 100 gigabyte!» Ma dentro pensavo a come mantenere un'espressione impassibile. Pensavi, sì, ragazzi sono bravi, e poi torni e ti ritrovi a barcamenarti con queste basi multiterabyte. Ed era così, essendo un junior. Potete immaginare quale colpo sia stato?

Conosco più di 20 linguaggi di programmazione. È ciò in cui ho dovuto districarmi durante il lavoro. Ti danno codice in Erlang, in C, in C++, in Lua, in Python, in Ruby e in qualcos'altro, e devi lavorarci su. In generale, è stato necessario. Non sono riuscito a contare esattamente, ma si è perso intorno a un numero di circa 20.

Poiché tutti presenti sanno cos'è Parallels e cosa facciamo, non parlerò di quanto siamo bravi e di cosa facciamo. Dirò solo che abbiamo 13 uffici nel mondo, più di 300 dipendenti, con sviluppo a Mosca, Tallinn e a Malta. Se si desidera, è possibile trasferirsi a Malta se d'inverno fa freddo e si ha bisogno di scaldarsi.

Il nostro team specificamente lavora con Python 2. Siamo nel settore business e non abbiamo tempo per implementare tecnologie alla moda, quindi soffriamo. Utilizziamo Django, perché contiene tutto il necessario, e abbiamo scartato il superfluo. Abbiamo anche MySQL, Redis e NGINX. E ancora, abbiamo molte altre cose interessanti. Abbiamo MongoDB, abbiamo conigli che corrono, abbiamo di tutto e di più, ma non è il mio ambito e non mi occupo di questo.

OpenResty

Ho parlato di me. Adesso vediamo di cosa parlerò oggi:

  • Cos'è OpenResty e come si utilizza?
  • Perché inventare un'altra bicicletta, quando abbiamo Python, NodeJS, PHP, Go e altre tecnologie fantastiche di cui tutti sono soddisfatti?
  • E qualche esempio dalla vita reale. Ho dovuto ridurre notevolmente la mia presentazione, perché era di 3,5 ore, quindi gli esempi saranno pochi.

OpenResty è NGINX. Grazie a questo, abbiamo un server web completo, ben progettato e veloce. Credo che la maggior parte di noi utilizzi NGINX in produzione. Sapete tutti che è veloce e potente. Ha un'ottima gestione dell'input/output sincrono, quindi non dobbiamo reinventare la ruota come è successo con gevent in Python. Gevent è fantastico, ma se scrivete codice in C e qualcosa va storto, con gevent impazzirete a fare debugging. Ho avuto un'esperienza: ci sono voluti addirittura due giorni per capire cosa fosse andato storto. Se nessuno avesse passato settimane a indagare, non avesse trovato il problema, e Google non l'avesse scoperto, saremmo stati davvero nei guai.

In NGINX, caching and static content management are already built-in. You won't have to worry about configuring it manually to prevent slowdowns or losing descriptors. NGINX is easy to deploy; there's no need to think about whether to use WSGI, PHP-FPM, Gunicorn, or Unicorn. Just set up NGINX, hand it off to your admins, and they know how to work with it. NGINX processes requests in a structured way. I'll explain this in more detail later. Briefly, it has phases: one when it receives the request, one when it processes it, and one when it delivers content to the user.

NGINX is fantastic, but there's one issue: it's not flexible enough despite all the terrific features the team has packed into the configuration. The power it offers falls short. That's why the folks at Taobao, about eight years ago, integrated Lua into it. What does that provide?

  • Dimension. It’s compact. LuaJIT adds around 100-200 kilobytes of memory overhead and minimal performance overhead.
  • Velocità. L'interprete LuaJIT è simile al C in molte situazioni, in altre perde contro Java e in alcune invece la supera. Per un certo periodo è stato considerato all'avanguardia, il miglior JIT compiler. Ora ci sono alternative più performanti, ma sono molto pesanti, come ad esempio V8. Alcuni interpreti JS e il HotSpot di Java sono più veloci in determinati casi, ma in altre situazioni restano inferiori.
  • Facilità di apprendimento. Se la vostra base di codice è scritta in Perl, e non siete Booking, troverete difficoltà a trovare programmatori Perl. Perché non ci sono, sono tutti a disposizione altrove, e formare nuovi programmatori richiede tempo e impegno. Se cercate programmatori in altri linguaggi, anche loro potrebbero necessitare di una riqualificazione o di una ricerca specifica. Nel caso di Lua è semplice. Chiunque può imparare Lua in tre giorni. A me sono bastate circa due ore per prendere confidenza. Dopo due ore scrivevo già codice in produzione. Dopo una settimana, era già in produzione.

Il risultato è questo:

OpenResty: trasformiamo NGINX in un server applicativo completo

C'è tanto materiale. OpenResty ha raccolto un sacco di moduli, sia di Lua che di engine. E avete tutto pronto: basta deployare ed è operativo.

Esempi

Basta con la teoria, passiamo al codice. Ecco un piccolo Hello World:

OpenResty: trasformiamo NGINX in un server applicativo completo

Cosa c'è qui? Questa è una location di engine. Non ci preoccupiamo, non scriviamo il nostro routing, non utilizziamo qualcosa di pronto — abbiamo già tutto in NGINX, viviamo bene e comodamente.

content_by_lua_block è un blocco che indica che restituiamo contenuto tramite uno script Lua. Prendiamo la variabile di engine remote_addr e la inseriamo in string.format. È la stessa cosa di sprintf, solo su Lua, solo che corretto. E lo restituiamo al client.

Il risultato apparirà così:

OpenResty: trasformiamo NGINX in un server applicativo completo

Ma torniamo al mondo reale. In produzione nessuno distribuisce Hello World. Di solito, la nostra applicazione interroga un database o altro e per la maggior parte del tempo aspetta una risposta.

OpenResty: trasformiamo NGINX in un server applicativo completo

Semplicemente aspetta e basta. Non è molto buono. Quando arrivano 100.000 utenti, è davvero difficile per noi. Quindi, come esempio, sviluppiamo un'applicazione semplice. Cercheremo immagini, ad esempio, di gattini. Solo che non ci limiteremo a cercare, espanderemo le parole chiave e, se un utente cerca "gattini", troveremo gatti, peluches e altro. Per iniziare, dobbiamo ricevere i dati della richiesta sul backend. Ecco come appare:

OpenResty: trasformiamo NGINX in un server applicativo completo

Due righe ti permettono di ottenere i parametri GET, senza complicazioni. Successivamente, supponiamo di recuperare queste informazioni da un database con una tabella chiave per parola e estensione usando una normale query SQL. È semplice. Si presenta così:

OpenResty: trasformiamo NGINX in un server applicativo completo

Colleghiamo la libreria resty.mysql, che abbiamo già incluso. Non dobbiamo installare nulla, è tutto pronto. Indichiamo come connetterci e facciamo una query SQL:

OpenResty: trasformiamo NGINX in un server applicativo completo

Qui è un po' spaventoso, ma tutto funziona. Qui 10 è il limite. Recuperiamo 10 record, siamo pigri e non vogliamo mostrarne di più. Ho dimenticato di menzionare il limite in SQL.

Poi troviamo le immagini per tutte le richieste. Raccogliamo un insieme di richieste e riempiamo una tabella Lua chiamata reqs, e facciamo ngx.location.capture_multi.

OpenResty: trasformiamo NGINX in un server applicativo completo

Tutte queste richieste vanno in parallelo e ci vengono restituiti i responi. Il tempo di esecuzione corrisponde al tempo di risposta del più lento. Se tutte vengono eseguite in 50 millisecondi e abbiamo inviato cento richieste, la risposta arriverà in 50 millisecondi.

Poiché siamo pigri e non vogliamo gestire HTTP e caching, faremo in modo che NGINX si occupi di tutto per noi. Come avete visto, c'era una richiesta su url/fetch, ecco qui:

OpenResty: trasformiamo NGINX in un server applicativo completo

Facciamo un semplice proxy_pass, indichiamo dove memorizzare in cache, come farlo e tutto funziona perfettamente.

Ma non è sufficiente, dobbiamo anche restituire i dati all'utente. L'idea più semplice è serializzare tutto in JSON, facile, in due righe. Restituiamo il Content-Type e restituiamo JSON.

Ma c'è una complicazione: l'utente non vuole leggere il JSON. Dobbiamo coinvolgere i frontend developer. A volte non abbiamo voglia di farlo all'inizio. E i SEO diranno che se stiamo cercando immagini, a loro non interessa. Ma se forniamo un contenuto, diranno che i motori di ricerca non indicizzano nulla.

Cosa fare al riguardo? Naturalmente, restituiremo all'utente HTML. Generare manualmente non è praticabile, quindi vogliamo usare i modelli. A tal fine, esiste una libreria lua-resty-template.

OpenResty: trasformiamo NGINX in un server applicativo completo

Probabilmente hai notato le tre spaventose lettere OPM. OpenResty viene fornito con il suo gestore di pacchetti, tramite cui è possibile installare un sacco di altri moduli, in particolare, lua-resty-template. È un semplice motore di template, simile ai template di Django. Qui puoi scrivere codice e sostituire variabili.

Di conseguenza, tutto apparirà più o meno così:

OpenResty: trasformiamo NGINX in un server applicativo completo

Abbiamo preso i dati e abbiamo reso il modello nuovamente in due righe. L'utente è felice, ha ricevuto dei gattini. Poiché abbiamo ampliato la richiesta, ha ricevuto anche un foca. Chissà, forse stava cercando proprio quello, ma non sapeva formulare correttamente la sua richiesta.

Tutto è fantastico, ma siamo in sviluppo e non vogliamo ancora mostrarlo agli utenti. Facciamo l'autenticazione. Per fare ciò, diamo un'occhiata a come NGINX gestisce la richiesta in termini di OpenResty:

  • Fase uno — access, quando l'utente è appena arrivato e lo abbiamo esaminato tramite intestazioni, indirizzo IP e altri dati. Possiamo immediatamente bloccarlò se non ci piace. Questo può essere utilizzato per l'autenticazione oppure, se riceviamo molte richieste, possiamo facilmente bloccarle in questa fase.
  • rewrite. Riscriviamo alcuni dati della richiesta.
  • content. Restituiamo il contenuto all'utente.
  • headers filter. Sostituiamo le intestazioni della risposta. Se abbiamo utilizzato proxy_pass, possiamo riscrivere alcune intestazioni prima di restituirle all'utente.
  • body filter. Possiamo sostituire il corpo.
  • log — registrazione. Possiamo scrivere i log in elasticsearch senza uno strato aggiuntivo.

La nostra autorizzazione apparirà più o meno così:

OpenResty: trasformiamo NGINX in un server applicativo completo

Lo aggiungeremo a questa location, che abbiamo descritto in precedenza, e inseriremo il seguente codice:

OpenResty: trasformiamo NGINX in un server applicativo completo

Controlliamo se abbiamo un cookie token. Se non c'è, reindirizziamo all'autorizzazione. Gli utenti sono astuti e potrebbero intuire che è necessario impostare il cookie token. Perciò lo inseriremo anche in Redis:

OpenResty: trasformiamo NGINX in un server applicativo completo

Il codice per interagire con Redis è molto semplice e non si discosta da altri linguaggi. Tutta l'input/output, sia lì che qui, è non bloccante. Se scrivete codice sincrono, funzionerà in modo asincrono. Un po' come con gevent, ma realizzato come si deve.

OpenResty: trasformiamo NGINX in un server applicativo completo

Procediamo con l'autorizzazione:

OpenResty: trasformiamo NGINX in un server applicativo completo

Diciamo che dobbiamo leggere il corpo della richiesta. Otteniamo gli argomenti POST, verifichiamo che il login e la password siano corretti. Se non sono corretti, reindirizziamo all'autorizzazione. Se sono corretti, scriviamo il token in Redis:

OpenResty: trasformiamo NGINX in un server applicativo completo

Non dimentichiamo di impostare il cookie, anche questo si fa in due righe:

OpenResty: trasformiamo NGINX in un server applicativo completo

Un esempio semplice e teorico. Naturalmente non creeremo un servizio che mostri gattini alle persone. Anche se chi lo sa. Quindi diamo un'occhiata a cosa è possibile fare in produzione.

  • Backend minimalista. A volte abbiamo bisogno di fornire solo un po' di dati nel backend: a volte dobbiamo inserire una data, altre volte mostrare un elenco, indicare quanti utenti ci sono attualmente sul sito, implementare un contatore o statistiche. Qualcosa di semplice. Piccole porzioni possono essere realizzate molto facilmente. Con questo possiamo lavorare in modo rapido, semplice e efficace.
  • Pre-elaborazione dei dati. A volte vogliamo integrare della pubblicità nella nostra pagina, e questa pubblicità viene recuperata tramite API. È molto facile farlo proprio qui. Non sovraccarichiamo il nostro backend, che sta già lavorando duramente. Possiamo raccogliere tutto qui. Possiamo assemblare del codice JS o, al contrario, separarlo, elaborando qualcosa prima di presentarlo all'utente.
  • Facciata per microservizi. Questo è un ottimo caso d'uso, l'ho realizzato. In precedenza lavoravo per una società chiamata Tenzor, che si occupa di rapporti elettronici e fornisce reportistica per circa metà delle aziende nel paese. Abbiamo creato un servizio in cui molte operazioni sono state realizzate utilizzando questo stesso meccanismo: routing, autorizzazione e altro.
    OpenResty può essere utilizzato come collante per i tuoi microservizi, fornendo un accesso unico a tutto e un'interfaccia unificata. Poiché i microservizi possono essere scritti in modo tale da avere qui Node.js, qui PHP, qui Python, e qui una cosa in Erlang, comprendiamo che non vogliamo riscrivere lo stesso codice ovunque. Quindi, OpenResty può essere integrato nel frontend.
  • Statistiche e analisi. Di solito NGINX si trova all'ingresso, e tutte le richieste passano attraverso di esso. È proprio qui che è molto comodo raccogliere dati. Si può calcolare qualcosa subito e inviarlo da qualche parte, per esempio a Elasticsearch, Logstash o semplicemente registrarlo in un log e poi inviarlo a destinazione.
  • Sistemi multiplayer. Ad esempio, è molto efficace per realizzare giochi online. Oggi a Cape Town, Alexander Gladys parlerà di come prototipare rapidamente un gioco multiplayer usando OpenResty.
  • Filtraggio delle richieste (WAF). Oggi è di moda utilizzare vari firewall per applicazioni web, ci sono molti servizi che li offrono. Con OpenResty, puoi creare un firewall per applicazioni web in grado di filtrare facilmente le richieste secondo le tue necessità. Se utilizzi Python, sai che PHP non verrà mai iniettato se non lo esegui da console. Sei consapevole di avere MySQL e Python. Probabilmente, qui potrebbero provare a eseguire un attacco di directory traversal e iniettare qualcosa nel database. Pertanto, puoi filtrare rapidamente e a basso costo le richieste sospette direttamente sul front-end.
  • Comunità. Poiché OpenResty è basato su NGINX, ha un vantaggio in più: è la comunità NGINX. È molto ampia e una buona parte delle domande che potresti avere all'inizio sono già state risolte dalla comunità NGINX.

    Sviluppatori Lua. Ieri ho parlato con ragazzi che hanno partecipato alla giornata di formazione HighLoad++ e ho sentito dire che Tarantool è scritto solo in Lua. Non è vero, in Lua sono scritti molte cose. Esempi: OpenResty, server XMPP Prosody, motore di gioco Love2D, script Lua in Warcraft e in altri luoghi. Ci sono molti sviluppatori Lua, hanno una comunità ampia e reattiva. Tutte le mie domande su Lua sono state risolte in poche ore. Quando scrivo alla mailing list, dopo pochi minuti ricevo una valanga di risposte che spiegano tutto nei dettagli. È fantastico. Purtroppo, non ovunque trovi una comunità così disponibile.
    Per OpenResty c'è GitHub, dove puoi aprire un issue se qualcosa non funziona. C'è una mailing list su Google Groups dove puoi discutere questioni generali, e una mailing list in cinese — magari, non parli inglese ma conosci il cinese.

Risultati

  • Spero di essere riuscito a far capire che OpenResty è un framework molto comodo, progettato per il web.
  • Ha una bassa barriera d'ingresso, poiché il codice è simile a quello che scriviamo, il linguaggio è abbastanza semplice e minimalista.
  • Fornisce I/O asincrono senza callback, quindi non avremo il caos di codice che a volte possiamo scrivere in NodeJS.
  • Ha un deployment semplice, poiché abbiamo bisogno solo di NGINX con il modulo richiesto e il nostro codice, e tutto funziona immediatamente.
  • Una grande e reattiva comunità.

Non ho spiegato in dettaglio come avviene il routing, sarebbe venuta una lunga storia.

Grazie per l'attenzione!

Riproduci video

Vladimir Protasov — OpenResty: trasformiamo NGINX in un server applicazioni completo

Fonte: habr.com
Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster