Nous publions à nouveau la transcription de la présentation lors de la conférence de 2016, qui s'est tenue à Skolkovo, prÚs de Moscou, les 7 et 8 novembre de l'année derniÚre. explique comment étendre les fonctionnalités de NGINX avec OpenResty et Lua.
Bonjour Ă tous, je m'appelle Vladimir Protasov, je travaille chez Parallels. Permettez-moi de me prĂ©senter. J'ai passĂ© les trois quarts de ma vie Ă Ă©crire du code. Je suis devenu programmeur dans tous les sens du terme : parfois, je vois du code mĂȘme dans mes rĂȘves. Le quart de ma vie est dĂ©diĂ© au dĂ©veloppement industriel, l'Ă©criture de code qui va directement en production. Le code que certains d'entre vous utilisent sans mĂȘme s'en rendre compte.
Pour que vous compreniez Ă quel point c'Ă©tait difficile. Quand j'Ă©tais un jeune junior, je suis arrivĂ©, et on m'a donnĂ© des bases de donnĂ©es de deux tĂ©raoctets. Maintenant, tout le monde ici a des charges Ă©levĂ©es. J'allais aux confĂ©rences, demandant : « Les gars, parlez-moi de votre big data, c'est gĂ©nial ? Quelle taille ont vos bases ? » Ils rĂ©pondaient : « Nous avons 100 gigaoctets ! » Je disais : « Super, 100 gigaoctets ! » Et en pensant pour moi-mĂȘme, comment garder un visage impassible. Vous vous dites, oui, ces gars-lĂ sont impressionnants, puis vous revenez et vous luttez avec ces bases de donnĂ©es multi-tĂ©raoctets. Et c'Ă©tait quand j'Ă©tais junior. Vous imaginez quel coup c'Ă©tait ?
Je connais plus de 20 langages de programmation. C'est ce avec quoi j'ai dû m'attaquer au travail. On vous donne du code en Erlang, en C, en C++, en Lua, en Python, en Ruby, et vous devez tout traiter. En gros, il a fallu s'en occuper. Je n'ai jamais pu compter avec précision, mais j'ai perdu le compte autour de 20.
Puisque tout le monde ici sait ce qu'est Parallels et ce que nous faisons, je ne vais pas parler de notre génialité et de nos activités. Je vais simplement dire que nous avons 13 bureaux dans le monde, plus de 300 employés, avec le développement à Moscou, à Tallinn et à Malte. Si vous souhaitez, vous pouvez déménager à Malte, si l'hiver est trop froid et que vous voulez vous réchauffer.
Notre dĂ©partement Ă©crit spĂ©cifiquement en Python 2. Nous sommes en affaires et nous n'avons pas le temps d'implĂ©menter des technologies Ă la mode, donc nous en souffrons. Nous avons Django, car il a tout ce dont nous avons besoin, et nous avons simplement Ă©cartĂ© l'excĂšs. Ăgalement MySQL, Redis et NGINX. Nous avons aussi beaucoup d'autres outils intĂ©ressants. Nous avons MongoDB, des lapins qui courent, nous avons de tout â mais ce n'est pas moi qui m'en occupe.
OpenResty
J'ai parlé de moi. Voyons maintenant de quoi je vais parler aujourd'hui :
- Qu'est-ce qu'OpenResty et à quoi ça sert ?
- Pourquoi réinventer la roue alors que nous avons Python, NodeJS, PHP, Go et d'autres outils formidables dont tout le monde est satisfait ?
- Et un petit exemple de la vie réelle. J'ai dû réduire considérablement ma présentation car elle durait 3,5 heures, donc il y aura peu d'exemples.
OpenResty est NGINX. Grùce à lui, nous avons un serveur web complet, bien écrit et rapide. Je pense que la plupart d'entre nous utilisent NGINX en production. Vous savez tous qu'il est rapide et formidable. Il inclut une entrée/sortie synchronisée de qualité, donc nous n'avons pas besoin de réinventer la roue comme ce fut le cas avec gevent en Python. Gevent est super, génial, mais si vous écrivez du code en C et que quelque chose ne va pas, vous aurez du mal à le déboguer avec gevent. J'ai eu cette expérience : il m'a fallu deux jours pour comprendre ce qui n'allait pas. Si quelqu'un n'avait pas fouillé pendant plusieurs semaines pour trouver le problÚme, ne l'avait pas publié sur Internet et que Google ne l'ait pas trouvé, nous aurions complÚtement perdu les pédales.
NGINX gĂšre dĂ©jĂ le cache et le contenu statique. Vous n'avez pas besoin de vous casser la tĂȘte sur la maniĂšre de le faire correctement, afin qu'il n'y ait pas de ralentissements ou que vous ne perdiez pas de descripteurs. Nginx est trĂšs pratique Ă dĂ©ployer, vous ne devez pas vous soucier de quel outil utiliser â WSGI, PHP-FPM, Gunicorn, Unicorn. Nginx est installĂ©, remis aux administrateurs, qui savent comment travailler avec. Nginx traite les requĂȘtes de maniĂšre structurĂ©e. J'en parlerai un peu plus tard. En rĂ©sumĂ©, il y a une phase lorsque la requĂȘte est reçue, une autre lorsque elle est traitĂ©e, et enfin lorsque le contenu est envoyĂ© Ă l'utilisateur.
Nginx est gĂ©nial, mais il a un petit problĂšme : il n'est pas assez flexible, mĂȘme avec toutes les fonctionnalitĂ©s impressionnantes que les dĂ©veloppeurs ont intĂ©grĂ©es dans la configuration, malgrĂ© les possibilitĂ©s de personnalisation. Cette puissance manque. C'est pourquoi les dĂ©veloppeurs de Taobao ont intĂ©grĂ© Lua il y a longtemps, il me semble il y a huit ans. Que permet-il ?
- Taille. Il est léger. LuaJIT ajoute environ 100-200 Ko de surcharge mémoire et une surcharge minimale en termes de performance.
- Vitesse. L'interpréteur LuaJIT est proche de C dans de nombreuses situations, dans certains cas, il est moins performant que Java, et dans d'autres, il la devance. Pendant un certain temps, il était considéré comme le meilleur, un compilateur JIT exceptionnel. Actuellement, il existe des compilateurs plus performants, mais ils sont trÚs lourds, comme V8. Certains interprÚtes JavaScript et le HotSpot de Java sont plus rapides dans certains cas, mais restent encore lents dans d'autres.
- SimplicitĂ© d'apprentissage. Si par exemple, vous avez une base de code en Perl et que vous n'ĂȘtes pas Booking, vous ne trouverez pas de programmeurs Perl. Parce qu'ils n'existent pas, tous ont Ă©tĂ© rĂ©cupĂ©rĂ©s et il est long et difficile de les former. Si vous recherchez des programmeurs dans un autre langage, il se peut que vous deviez Ă©galement les requalifier ou les trouver. Pour Lua, c'est simple. Un dĂ©veloppeur junior peut apprendre Lua en trois jours. Il m'a fallu environ deux heures pour le comprendre. AprĂšs deux heures, j'Ă©crivais dĂ©jĂ du code en production. Environ une semaine plus tard, il a Ă©tĂ© mis en production.
En conséquence, cela ressemble à ceci :

Il y a beaucoup de choses ici. OpenResty a regroupĂ© des modules, Ă la fois Lua et Engine. Et tout est prĂȘt - dĂ©ployĂ© et ça fonctionne.
Exemples
Assez de lyrisme, passons au code. Voici un petit Hello World :

Qu'y a-t-il ici ? C'est un location d'Engine. Nous ne nous compliquons pas la vie, nous n'écrivons pas notre propre routage, nous n'utilisons pas un routage déjà existant - nous avons déjà ça dans NGINX, nous vivons bien et paresseusement.
content_by_lua_block â c'est un bloc qui indique que nous livrons le contenu Ă l'aide d'un script Lua. Nous prenons la variable d'Engine remote_addr et nous l'insĂ©rons dans string.format. C'est la mĂȘme chose que sprintf, mais en Lua, seulement correcte. Et nous le fournissons au client.
En conséquence, cela apparaßtra comme ceci :

Mais revenons au monde réel. En production, personne ne déploie Hello World. Nos applications vont généralement se connecter à une base de données ou à un autre service et passent la plupart du temps à attendre une réponse.

Elles attendent simplement. Ce n'est pas trĂšs efficace. Lorsque 100 000 utilisateurs arrivent, c'est trĂšs difficile pour nous. Donc, prenons comme exemple une application simple. Nous allons chercher des images, par exemple, de chats. Mais nous ne allons pas simplement chercher, nous allons Ă©largir les mots-clĂ©s et, si un utilisateur recherche "chatons", nous lui trouverons des chats, des peluches, etc. Pour commencer, nous devons obtenir les donnĂ©es de la requĂȘte sur le backend. Voici Ă quoi cela ressemble :

Deux lignes vous permettent de rĂ©cupĂ©rer les paramĂštres GET, rien de compliquĂ©. Ensuite, nous allons, par exemple, nous connecter Ă la base de donnĂ©es avec une table pour les mots-clĂ©s et les extensions en effectuant une simple requĂȘte SQL pour obtenir ces informations. C'est facile. Voici Ă quoi cela ressemble :

Nous connectons la bibliothĂšque resty.mysql, qui est dĂ©jĂ incluse dans notre ensemble. Nous n'avons rien Ă installer, tout est prĂȘt. Nous spĂ©cifions comment nous connecter et faisons la requĂȘte SQL :

C'est un peu effrayant ici, mais tout fonctionne. Ici, 10 - c'est la limite. Nous extrayons 10 enregistrements, nous sommes paresseux, nous ne voulons pas en montrer plus. J'ai oublié de parler de la limite en SQL.
Ensuite, nous trouvons des images pour toutes les requĂȘtes. Nous collectons un ensemble de requĂȘtes et remplissons une table Lua qui s'appelle reqs, et faisons ngx.location.capture_multi.

Toutes ces requĂȘtes sont envoyĂ©es en parallĂšle, et nous recevons des rĂ©ponses. Le temps d'exĂ©cution est Ă©gal au temps de rĂ©ponse du plus lent. Si nous avons toutes les rĂ©ponses en 50 millisecondes, et que nous avons envoyĂ© une centaine de requĂȘtes, la rĂ©ponse nous parviendra en 50 millisecondes.
Puisque nous sommes paresseux et que nous ne voulons pas Ă©crire de traitement HTTP et de mise en cache, nous allons faire en sorte que NGINX fasse tout pour nous. Comme vous l'avez vu, il y avait une requĂȘte pour url/fetch, la voici :

Nous faisons un simple proxy_pass, en indiquant oĂč mettre en cache, comment le faire, et tout fonctionne.
Mais cela ne suffit pas, nous devons aussi remettre les données à l'utilisateur. L'idée la plus simple est de tout sérialiser en JSON, facilement, en deux lignes. On donne le Content-Type, on renvoie le JSON.
Mais il y a une difficulté : l'utilisateur ne veut pas lire le JSON. Il faut attirer les front-enders. Parfois, nous ne voulons pas vraiment faire cela au départ. Et puis les SEO diront que s'ils cherchent des images, cela leur est égal. Mais s'ils reçoivent un contenu quelconque, ils diront que nos moteurs de recherche n'indexent rien.
Que faire avec ça ? Naturellement, nous allons envoyer HTML à l'utilisateur. Générer manuellement - ce n'est pas idéal, donc nous voulons utiliser des modÚles. Pour cela, il y a une bibliothÚque lua-resty-template.

Vous avez probablement vu les trois lettres effrayantes OPM. OpenResty vient avec son propre gestionnaire de paquets, à travers lequel on peut installer une foule d'autres modules, notamment lua-resty-template. C'est un moteur de modÚles simple, proche des modÚles Django. On peut écrire du code et substitution de variables.
En fin de compte, tout ressemblera Ă ceci :

Nous avons pris les donnĂ©es et rendu le modĂšle en deux lignes. L'utilisateur est heureux, il a reçu des chatons. Comme nous avons Ă©largi la requĂȘte, il a aussi reçu un veau de mer. Qui sait, peut-ĂȘtre qu'il le cherchait mais ne pouvait pas formuler correctement sa demande.
C'est gĂ©nial, mais nous sommes en dĂ©veloppement, et nous ne voulons pas encore montrer cela aux utilisateurs. Faisons une autorisation. Pour ce faire, voyons comment NGINX traite la requĂȘte en termes d'OpenResty :
- La premiĂšre phase - un access, quand l'utilisateur vient juste d'arriver, et que nous l'avons regardĂ© Ă travers les en-tĂȘtes, l'adresse IP, et d'autres donnĂ©es. Nous pouvons immĂ©diatement le couper s'il ne nous plaĂźt pas. Cela peut ĂȘtre utilisĂ© pour l'autorisation, ou si nous recevons trop de requĂȘtes, nous pouvons facilement les couper Ă cette phase.
- rewrite. Nous réécrivons certaines donnĂ©es de la requĂȘte.
- contenu. Nous donnons le contenu Ă l'utilisateur.
- filtre des en-tĂȘtes. Nous modifions les en-tĂȘtes de la rĂ©ponse. Si nous avons utilisĂ©
proxy_pass, nous pouvons réécrire certains en-tĂȘtes avant de les donner Ă l'utilisateur. - filtre du corps. Nous pouvons modifier le corps.
- log â journalisation. Il est possible d'Ă©crire des journaux dans elasticsearch sans couche supplĂ©mentaire.
Notre autorisation ressemblera Ă peu prĂšs Ă cela :

Nous allons l'ajouter à celui location, que nous avons décrit auparavant, et y insérer ce code :

Nous vérifions si nous avons un cookie token. S'il n'y en a pas, nous dirigeons vers l'autorisation. Les utilisateurs sont futés et peuvent deviner qu'il faut mettre un cookie token. Donc, nous allons aussi le placer dans Redis :

Le code de travail avec Redis est trÚs simple et ne diffÚre en rien des autres langages. De plus, toutes les entrées/sorties là -bas comme ici sont non-bloquantes. Si vous écrivez du code synchrone, cela fonctionne de maniÚre asynchrone. Un peu comme avec gevent, mais fait correctement.

Faisons l'autorisation elle-mĂȘme :

Nous disons que nous devons lire le corps de la requĂȘte. Nous obtenons les arguments POST, vĂ©rifions que le login et le mot de passe sont corrects. S'ils ne le sont pas, nous dirigeons vers l'autorisation. Mais s'ils sont corrects, nous Ă©crivons le token dans Redis :

N'oublions pas de placer un cookie, cela se fait également en deux lignes :

C'est un exemple simple, thĂ©orique. Bien sĂ»r, nous ne ferons pas un service qui montre des chatons aux gens. Bien que qui sait. Donc, parcourons ce qui peut ĂȘtre fait en production.
- Backend minimaliste. Parfois, il nous faut fournir trĂšs peu de donnĂ©es au backend : parfois, il faut juste insĂ©rer une date, afficher une liste quelconque, dire combien d'utilisateurs sont sur le site, ajouter un compteur ou des statistiques. Quelque chose de petit. Des morceaux minimaux peuvent ĂȘtre trĂšs facilement rĂ©alisĂ©s. Cela permettra d'aller vite, facilement et efficacement.
- PrĂ©traitement des donnĂ©es. Parfois, nous voulons intĂ©grer de la publicitĂ© dans notre page, et nous obtenons cette publicitĂ© via des requĂȘtes API. C'est trĂšs facile Ă faire ici. Nous ne surchargeons pas notre backend, qui travaille dĂ©jĂ dur. Nous pouvons le prendre et le collecter ici. Nous pouvons crĂ©er des JS ou, au contraire, les dĂ©composer, prĂ©-traiter quelque chose avant de le remettre Ă l'utilisateur.
- Facade pour microservice. C'est aussi un trĂšs bon cas d'utilisation, que j'ai rĂ©alisĂ©. Auparavant, je travaillais dans une entreprise nommĂ©e Tenzor, qui s'occupe de la dĂ©claration Ă©lectronique, fournissant des rapports pour environ la moitiĂ© des personnes morales dans le pays. Nous avons dĂ©veloppĂ© un service, oĂč beaucoup de choses ont Ă©tĂ© rĂ©alisĂ©es grĂące Ă ce mĂȘme mĂ©canisme : routage, autorisation et autres.
OpenResty peut ĂȘtre utilisĂ© comme un lien pour vos microservices, qui offrira un accĂšs unifiĂ© Ă tout et une interface unique. Ătant donnĂ© que les microservices peuvent ĂȘtre Ă©crits dans diffĂ©rents langages â ici vous avez Node.js, lĂ vous avez PHP, ici Python, et une chose en Erlang â nous comprenons que nous ne voulons pas réécrire le mĂȘme code partout. C'est pourquoi vous pouvez mettre OpenResty Ă l'avant. - Statistiques et analyses. En gĂ©nĂ©ral, NGINX se trouve Ă l'entrĂ©e, et toutes les requĂȘtes le traversent. C'est ici qu'il est trĂšs pratique de rassembler. Vous pouvez immĂ©diatement calculer quelque chose et l'envoyer quelque part, par exemple vers Elasticsearch, Logstash ou simplement l'enregistrer dans un journal et ensuite l'envoyer ailleurs.
- SystÚmes multijoueurs. Par exemple, les jeux en ligne sont également trÚs adaptés. Aujourd'hui à Cape Town, Alexandre Gladych parlera de la maniÚre de prototyper rapidement un jeu multijoueur à l'aide d'OpenResty.
- Filtrage des requĂȘtes (WAF). Actuellement, il est Ă la mode de crĂ©er divers pare-feux d'application web, il existe de nombreux services qui les fournissent. Avec OpenResty, vous pouvez crĂ©er un pare-feu d'application web qui filtrera facilement et simplement les requĂȘtes selon vos exigences. Si vous utilisez Python, vous savez que PHP ne pourra pas ĂȘtre injectĂ©, Ă moins que vous ne le lançiez depuis la console quelque part. Vous savez que vous avez MySQL et Python. Peut-ĂȘtre que certaines personnes pourraient tenter de rĂ©aliser un chemin de rĂ©pertoire et d'injecter quelque chose dans la base. Donc, vous pouvez filtrer rapidement et Ă moindre coĂ»t les requĂȘtes suspectes directement Ă l'avant.
- CommunautĂ©. Ătant donnĂ© qu'OpenResty est basĂ© sur NGINX, il a un avantage : c'est la communautĂ© NGINX. C'est trĂšs vaste, et une bonne partie des questions que vous pourriez avoir au dĂ©but a dĂ©jĂ Ă©tĂ© rĂ©solue par la communautĂ© NGINX.
Développeurs Lua. Hier, j'ai discuté avec des gars qui étaient venus pour la journée de formation HighLoad++ et j'ai entendu dire que Tarantool était le seul projet écrit en Lua. Ce n'est pas vrai, beaucoup de choses sont écrites en Lua. Exemples : OpenResty, le serveur XMPP Prosody, le moteur de jeu Love2D, Lua est scripté dans Warcraft et ailleurs. Il y a beaucoup de développeurs Lua, ils ont une grande communauté réactive. Toutes mes questions sur Lua ont été résolues en quelques heures. Lorsque l'on écrit sur la liste de diffusion, littéralement quelques minutes plus tard, on reçoit déjà de nombreuses réponses avec des explications sur comment et pourquoi. C'est vraiment super. Malheureusement, cette communauté bienveillante n'est pas présente partout.
Il y a un GitHub pour OpenResty, oĂč vous pouvez crĂ©er une issue si quelque chose ne fonctionne pas. Il existe une liste de diffusion sur Google Groups pour discuter des questions gĂ©nĂ©rales, ainsi qu'une mailing list en chinois - au cas oĂč vous ne maĂźtriseriez pas l'anglais mais que vous parliez chinois.
Résultats
- J'espĂšre avoir pu exprimer que OpenResty est un framework extrĂȘmement pratique, conçu pour le web.
- Il a une faible barriÚre à l'entrée, car le code ressemble à ce sur quoi nous écrivons, le langage est assez simple et minimaliste.
- Il offre une entrée/sortie asynchrone sans callbacks, nous n'aurons pas de spaghetti comme cela peut parfois arriver dans Node.js.
- Son déploiement est facile, car nous avons juste besoin de NGINX avec le module requis et de notre code, et tout fonctionne immédiatement.
- Une grande et réactive communauté.
Je n'ai pas expliqué en détail comment fonctionne le routage, cela aurait été trÚs long.
Merci de votre attention !

Source : habr.com
