Pourquoi écrivons-nous la logique métier dans Lua

Salut, Habr. Dans cet article, nous voulons parler de comment et pourquoi nous utilisons un langage de programmation avec le beau nom Lua chez IPONWEB.

Lua est un langage de programmation intégrable basé sur des scripts avec un interprète gratuit et open source en C. Il a été développé en 1993 au Brésil, dans la division Tecgraf de l'Université catholique de Rio de Janeiro, et ses ancêtres étaient DEL (Data-Entry Language) et SOL (Simple Object Language) développé précédemment. L'un des ancêtres, la langue SOL, a indirectement participé au «baptême» du nouveau-né - «Sol» est traduit du portugais par «soleil», et la nouvelle langue a été nommée «Lua», «lune».

La facilité d'intégration de Lua dans des moteurs écrits dans des langages «système» en a fait un langage de script populaire pour les jeux vidéo. Par exemple, les scripts sont écrits en Lua dans Grim Fandango et Baldur's Gate. Ceux qui jouent à World of Warcraft ont probablement entendu parler de Lua plus d'une ou deux fois - c'est là que des add-ons pour le jeu sont écrits qui facilitent la vie des joueurs hardcore, des fans occasionnels, des fans pour mesurer leur efficacité et des autres habitants du monde du jeu. En dehors de gamedev, Lua est utilisé comme langage de script des systèmes embarqués (téléviseurs, imprimantes, panneaux de voiture), ainsi que des applications, par exemple, le VLC Media Player. Lua utilise des outils tels que Tarantool, Redis et OpenResty en tant que langage intégré. Lua a également été utilisé comme langage d'extension pour les codes de calcul Fortran simulant le comportement thermomécanique du combustible nucléaire.

Pourquoi Lua?


IPONWEB est un développeur de plateformes très chargées pour les entreprises travaillant dans le domaine de la publicité en ligne: DSP, SSP, agences de publicité et annonceurs. Nous avons parlé en détail de notre travail dans cet article . Au début, nous avons développé la logique métier de nos plateformes en C ++, mais nous nous sommes vite rendu compte que ce n'était pas le meilleur choix. Pour minimiser les coûts, les performances de la plate-forme sont importantes, ainsi que la vitesse de développement, et le développement C ++ s'est avéré trop lent pour nous, et la complexité de l'ajout de fonctionnalités a également été affectée. Nous avons décidé d'isoler l'interprétation de la logique métier du code serveur de bas niveau, nous avons commencé à chercher un langage approprié pour cela et nous nous sommes installés sur Lua. C'était en 2008, les implémentations JavaScript qui nous convenaient n'existaient pas encore, Perl, Python et Ruby étaient trop lents et pas faciles à intégrer. Et il y avait le langage Lua, pas très célèbre, mais populaire dans le développement de jeux, et ce que nous voulions était similaire aux besoins des développeurs de jeux - nous avions besoin d'un moteur rapide pour les opérations de bas niveau et d'un langage rapide facile à construire pour la logique métier.

Lua est vraiment une langue très rapide. Une augmentation supplémentaire de la vitesse peut être obtenue en utilisant LuaJIT , l'environnement d'exécution de Lua 5.1, qui comprend un compilateur JIT traceur (nous utilisons notre propre fork, dont nous avons déjà parlé ). Puisque nous écrivons la logique métier pour les systèmes RTB , la vitesse est essentielle pour nous: en RTB, en moyenne, il y a 120 millisecondes pour traiter chaque demande entrante. Dans le même temps, seules 10 à 15 millisecondes sont allouées pour l'exécution de code et le reste du temps attend une réponse d'autres services. Si, par exemple, un utilisateur ne remarque même pas un retard d'une demi-seconde lors du chargement d'un site sur le réseau, alors pour RTB ces 500 millisecondes sont une période de temps énorme. La réponse à la question de la rapidité de Lua est qu'elle est suffisamment rapide pour que nous puissions y écrire de la logique métier pendant de nombreuses années et rester dans le domaine de la RTB. Si la langue que nous avons choisie n'était pas assez rapide, nous n'aurions personne pour écrire la plate-forme. Est-ce à dire que RTB ne peut pas être écrit dans d'autres langues? Ça ne veut pas dire. Mais nous écrivons RTB en Lua et gérons avec succès nos tâches commerciales et celles de nos clients. Un bon exemple de la vitesse de Lua sur le serveur est ce benchmark OpenResty.

Lua en tant que langage intégré présente de nombreux avantages: il est minimaliste, compact, avec une très petite bibliothèque standard. Sa fonctionnalité est complètement dupliquée en C, ce qui fournit une interaction facile et «transparente» entre Lua et C. Lua a un seuil de connexion assez bas par rapport à de nombreuses autres langues: la plupart des programmeurs qui viennent travailler dans IPONWEB n'ont jamais écrit sur Lua auparavant, mais ils en ont assez plusieurs jours pour s'engager pleinement dans le travail.

Voici un exemple simple de ciblage d'une audience publicitaire.

-- deal: ,  ,      --     ( ,    ..) -- imp:   (impression opportunity),  --       local function can_be_shown(deal, imp) local targeting = deal.targeting --    if not targeting then return true end --         -- (,    ,   ): if targeting.media_type then if not passes_targeting(targeting.media_type, imp.details.media_type) then return false end end --        : if targeting.size then if not passes_targeting(targeting.size, imp.details.sizes) then return false end end return true end 

Et voici à quoi ressemble un simple gestionnaire (gestionnaire d'événements).

 local adm_cache = require 'modules.adm_cache' -- adm = "ad markup" local config = require 'modules.config' local util = require 'modules.util' local AbstractHandler = require 'handlers.abstract' local ImpRenderHandler = AbstractHandler:new({is_server_request = false}) local IMP_RENDER_TEMPLATE = config.get('imp_render_template') local IMP_RENDER_BILLING = {name = 'free', type = 'in'} --   ,  . --   ( ,   ) . --          . function ImpRenderHandler:handle(params) local user_id = self.uuid local cache_id = get_param('id') if not cache_id or cache_id == '' then return self:process_bad_request({reason = '"id" parameter is expected', user_id = user_id}) end local adm = adm_cache.get(cache_id) if not adm then return self:process_bad_request({reason = 'No adm in cache', user_id = user_id}) end update_billing(IMP_RENDER_BILLING) self:log_request('imp_render', {adm = adm, user_id = user_id, cache_id = cache_id}) local content = util.expand_macro(IMP_RENDER_TEMPLATE, {ADM = adm}) return {200, content = content} end return ImpRenderHandler 

La simplicité de Lua permet non seulement un développement rapide, mais vous permet également de faire beaucoup de travail avec peu d'effort. La plate-forme IPONWEB est une solution générale adaptée aux besoins d'un client particulier, tandis qu'un développeur et un gestionnaire peuvent mener le projet. Lire le code sur Lua peut non seulement les développeurs eux-mêmes, mais aussi les chefs de projet, et parfois les clients. Ensemble, nous découvrons rapidement le problème, trouvons sa cause et sa solution. Souvent, le chef de projet informe le développeur du problème et suggère immédiatement un moyen de le résoudre.

Dans le même temps, la simplicité de Lua peut être trompeuse, tandis que le minimalisme et la compacité ont un inconvénient. Si le code est écrit, par exemple, en Perl ou Python, le développeur a à sa disposition d'énormes référentiels de modules prêts à l'emploi, Ruby a RubyGems, et de nombreux autres langages ont des référentiels riches. Et Lua a LuaRocks et les trois mille modules qui s'y trouvent. De plus, même si LuaRocks a le bon module, il est fort probable que vous devrez travailler dur pour l'utiliser dans une entreprise particulière. Lua fournit de bons outils pour créer un environnement sécurisé pour exécuter du code (bacs à sable), et lorsque vous travaillez dans des bacs à sable, certaines fonctions peuvent être désactivées. Cela signifie que les modules LuaRocks peuvent ne pas fonctionner s'ils utilisent des fonctions bloquées par l'environnement sécurisé de l'entreprise. C’est le prix de la compacité et de l’intégration, mais cela en vaut le prix - les langages avec batteries, comme par exemple Python, ne sont pas construits de manière plus compliquée que Lua.

Comment ça marche?


La base de notre plateforme est un serveur HTTP personnalisable avec une API pratique et extensible qui fournit un ensemble de fonctions pour le développeur Lua et est adapté aux tâches du marché publicitaire. Ce serveur traite des centaines de millions de demandes et écrit des téraoctets de journaux par jour. Les demandes entrantes sont réparties uniformément sur les threads système et à l'intérieur des threads système se trouvent des sandbox.

image

Lorsqu'une demande arrive sur le serveur, une coroutine est créée à l'intérieur du sandbox dans lequel cette demande est tombée, qui traite la demande. Les coroutines fonctionnent indépendamment les unes des autres, chaque coroutine créée est mise en file d'attente pour exécution. La durée de vie de chaque coroutine (le temps total de traitement de la demande, en tenant compte de la réponse attendue des services impliqués: base de données, métriques, serveur de budget) ne doit pas dépasser 120 millisecondes.



En bref, le processus de traitement des demandes peut être décrit comme suit:

  1. Chaque demande reçue est analysée et passe un contrôle standard pour l'exactitude.
  2. Lancement de Corutin, qui est responsable du traitement de cette demande. À l'intérieur de chaque bac à sable, il existe de nombreuses coroutines à différents statuts.
  3. Le traitement des demandes démarre, ce qui peut avoir deux résultats:
    • Le traitement s'est terminé avec succès.
    • Corutin transfère le contrôle au serveur. Cela se produit généralement lorsque coroutine attend une réponse d'autres services. Dans de tels cas, le travail de la corutine est suspendu jusqu'à ce qu'une réponse arrive ou que le temps d'attente expire. Lors du transfert de contrôle, le serveur commence à traiter la requête suivante. Il peut s'agir d'une nouvelle demande ou d'une demande qui reçoit une réponse de tous les services impliqués et est prête à continuer d'exécuter le code. L'ordre de traitement des demandes est déterminé par les exigences de la logique métier.


L'utilisation de la corutine est un autre sujet intéressant qui mérite une discussion détaillée. Par exemple, cet article détaille comment les corutines peuvent être utilisées pour créer des cinématiques dans les jeux vidéo. Et les coroutines sur le serveur d'applications devraient consacrer un article séparé, et peut-être à l'avenir nous le ferons.

Et ensuite?


Et puis, peut-être, l'utilisation de Lua dans IPONWEB sera étendue. Nous avons des idées sur la façon dont vous pouvez toujours utiliser Lua dans notre entreprise, et lorsque ces idées seront mises en œuvre, nous partagerons certainement de nouvelles expériences. La commodité et les capacités de Lua en tant que langage de script intégré peuvent nous aider, en particulier, à accélérer le traitement des données client. Mais cela reste du domaine des plans et des perspectives.

En résumé, nous pouvons dire que notre choix du langage de la logique métier, fait il y a 11 ans, continue de se justifier, nous permettant de faire face avec succès à nos propres tâches métier et de nous aider à résoudre les problèmes de nos clients. Facile à lire, facile à intégrer, rapide et facile à apprendre, Lua a été et reste l'un des meilleurs langages de script, dont la portée n'est en aucun cas limitée au développement de jeux. La logique métier IPONWEB écrite dessus n'en est qu'un exemple.

Source: https://habr.com/ru/post/fr469049/


All Articles