Warum schreiben wir Geschäftslogik in Lua?

Hallo Habr. In diesem Beitrag möchten wir darüber sprechen, wie und warum wir bei IPONWEB eine Programmiersprache mit dem schönen Namen Lua verwenden.

Lua ist eine skriptbasierte eingebettete Programmiersprache mit einem freien Interpreter und Open Source in C. Sie wurde 1993 in Brasilien in der Tecgraf-Abteilung der Katholischen Universität von Rio de Janeiro entwickelt. Ihre Vorfahren waren DEL (Data-Entry Language) und SOL (Simple Object Language) dort früher entwickelt. Einer der Vorfahren, die SOL-Sprache, nahm indirekt an der "Taufe" des Neugeborenen teil - "Sol" wird aus dem Portugiesischen als "Sonne" übersetzt, und die neue Sprache wurde "Lua", "Mond" genannt.

Die einfache Einbettung von Lua in Engines, die in „Systemsprachen“ geschrieben sind, hat es zu einer beliebten Skriptsprache für Videospiele gemacht. Zum Beispiel werden Skripte in Lua in Grim Fandango und Baldur's Gate geschrieben. Diejenigen, die World of Warcraft spielen, haben wahrscheinlich auch mehr als ein- oder zweimal von Lua gehört - dort werden Add-Ons für das Spiel geschrieben, die Hardcore-Spielern, Gelegenheitsfans, Fans das Messen ihrer Effektivität und anderen Bewohnern der Spielwelt das Leben erleichtern. Außerhalb von Gamedev wird Lua als Skriptsprache für eingebettete Systeme (Fernseher, Drucker, Autotafeln) sowie für Anwendungen wie den VLC Media Player verwendet. Lua verwendet Tools wie Tarantool, Redis und OpenResty als eingebettete Sprache. Lua wurde auch als Erweiterungssprache für Fortran-Berechnungscodes verwendet, die das thermomechanische Verhalten von Kernbrennstoffen simulieren.

Warum Lua?


IPONWEB ist Entwickler hoch geladener Plattformen für Unternehmen, die im Bereich Online-Werbung tätig sind: DSP, SSP, Werbeagenturen und Werbetreibende. Wir haben in diesem Artikel ausführlich über unsere Arbeit gesprochen. Zuerst haben wir die Geschäftslogik unserer Plattformen in C ++ entwickelt, aber schnell erkannt, dass dies nicht die beste Wahl ist. Um die Kosten zu minimieren, sind die Leistung der Plattform sowie die Entwicklungsgeschwindigkeit wichtig. Die C ++ - Entwicklung erwies sich für uns als zu langsam, und die Komplexität des Hinzufügens von Funktionen wurde ebenfalls beeinflusst. Wir beschlossen, die Interpretation der Geschäftslogik vom Servercode auf niedriger Ebene zu isolieren, suchten nach einer geeigneten Sprache dafür und entschieden uns für Lua. Es war im Jahr 2008, JavaScript-Implementierungen, die für uns geeignet waren, gab es noch nicht, Perl, Python und Ruby waren zu langsam und nicht einfach einzubetten. Und es gab die Lua-Sprache, die nicht sehr berühmt, aber bei Spielentwicklern beliebt war, und was wir wollten, ähnelte den Anforderungen von Spieleentwicklern - wir brauchten eine schnelle Engine für Operationen auf niedriger Ebene und eine einfach zu erstellende schnelle Sprache für Geschäftslogik.

Lua ist wirklich eine sehr schnelle Sprache. Eine zusätzliche Geschwindigkeitssteigerung kann mit LuaJIT erreicht werden, der Laufzeitumgebung für Lua 5.1, die einen Tracing-JIT-Compiler enthält (wir verwenden unseren eigenen Fork, über den wir bereits geschrieben haben ). Da wir Geschäftslogik für RTB- Systeme schreiben, ist die Geschwindigkeit für uns entscheidend: In RTB sind durchschnittlich 120 Millisekunden erforderlich, um jede eingehende Anforderung zu verarbeiten. Gleichzeitig werden nur 10 bis 15 Millisekunden für die Codeausführung zugewiesen, und der Rest der Zeit wartet auf eine Antwort von anderen Diensten. Wenn ein Benutzer beispielsweise beim Laden eines Standorts im Netzwerk nicht einmal eine Verzögerung von einer halben Sekunde bemerkt, sind diese 500 Millisekunden für RTB eine enorme Zeitspanne. Die Antwort auf die Frage, wie schnell Lua ist, ist, dass es schnell genug ist, damit wir viele Jahre lang Geschäftslogik darauf schreiben und im RTB-Geschäft bleiben können. Wenn die Sprache, die wir gewählt haben, nicht schnell genug wäre, hätten wir niemanden, für den wir die Plattform schreiben könnten. Bedeutet dies, dass RTB nicht in anderen Sprachen geschrieben werden kann? Bedeutet nicht. Aber wir schreiben RTB in Lua und bewältigen erfolgreich unsere und die Geschäftsaufgaben unserer Kunden. Ein gutes Beispiel für die Geschwindigkeit von Lua auf dem Server ist dieser OpenResty- Benchmark .

Lua als eingebettete Sprache hat viele Vorteile: Es ist minimalistisch, kompakt und verfügt über eine sehr kleine Standardbibliothek. Die Funktionalität ist in C vollständig dupliziert, was eine einfache und „nahtlose“ Interaktion zwischen Lua und C ermöglicht. Lua hat im Vergleich zu vielen anderen Sprachen einen relativ niedrigen Anmeldeschwellenwert: Die meisten Programmierer, die in IPONWEB arbeiten, haben noch nie an Lua geschrieben, aber sie haben genug mehrere Tage, um sich voll auf die Arbeit einzulassen.

Hier ist ein einfaches Beispiel für die Ausrichtung auf ein Werbepublikum.

-- 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 

Und so sieht ein einfacher Handler (Event-Handler) aus.

 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 

Die Einfachheit von Lua ermöglicht nicht nur eine schnelle Entwicklung, sondern ermöglicht es Ihnen auch, viel Arbeit mit wenig Aufwand zu erledigen. Die IPONWEB-Plattform ist eine allgemeine Lösung, die auf die Bedürfnisse eines bestimmten Kunden zugeschnitten ist, während ein Entwickler und ein Manager das Projekt durchführen können. Lesen Sie den Code auf Lua können nicht nur die Entwickler selbst, sondern auch Projektmanager und manchmal Kunden. Gemeinsam entdecken wir schnell das Problem, finden seine Ursache und Lösung. Oft informiert der Projektmanager den Entwickler über das Problem und schlägt sofort eine Lösung vor.

Gleichzeitig kann die Einfachheit von Lua täuschen, während Minimalismus und Kompaktheit einen Nachteil haben. Wenn der Code beispielsweise in Perl oder Python geschrieben ist, verfügt der Entwickler über riesige Repositorys mit vorgefertigten Modulen, Ruby über RubyGems und viele andere Sprachen über umfangreiche Repositorys. Und Lua hat LuaRocks und die dreitausend Module, die dort liegen. Selbst wenn LuaRocks über das richtige Modul verfügt, ist es sehr wahrscheinlich, dass Sie hart arbeiten müssen, um es in einem bestimmten Unternehmen zu verwenden. Lua bietet gute Tools zum Erstellen einer sicheren Umgebung für die Ausführung von Code (Sandboxen). Bei der Arbeit in Sandboxen können einige Funktionen deaktiviert werden. Dies bedeutet, dass LuaRocks-Module möglicherweise nicht funktionieren, wenn sie Funktionen verwenden, die von der sicheren Umgebung des Unternehmens blockiert werden. Dies ist der Preis für Kompaktheit und Einbettbarkeit, aber es ist den Preis wert - Sprachen mit Batterien wie beispielsweise Python sind nicht komplizierter als Lua.

Wie funktioniert es


Die Basis unserer Plattform ist ein anpassbarer HTTP-Server mit einer praktischen und erweiterbaren API, die dem Lua-Entwickler eine Reihe von Funktionen bietet und auf die Aufgaben des Werbemarkts zugeschnitten ist. Dieser Server verarbeitet Hunderte Millionen Anfragen und schreibt Terabyte an Protokollen pro Tag. Eingehende Anforderungen werden gleichmäßig auf die Systemthreads verteilt, und innerhalb der Systemthreads befinden sich Sandboxen.

Bild

Wenn eine Anforderung auf dem Server eintrifft, wird in der Sandbox, in die diese Anforderung gefallen ist, eine Coroutine erstellt, die die Anforderung verarbeitet. Coroutinen arbeiten unabhängig voneinander. Jede erstellte Coroutine wird zur Ausführung in die Warteschlange gestellt. Die Lebensdauer jeder Coroutine (die Gesamtverarbeitungszeit der Anforderung unter Berücksichtigung der erwarteten Antwort der beteiligten Dienste: Datenbank, Metriken, Budgetserver) sollte 120 Millisekunden nicht überschreiten.



Kurz gesagt kann der Anforderungsverarbeitungsprozess wie folgt beschrieben werden:

  1. Jede empfangene Anfrage wird analysiert und besteht eine Standardprüfung auf Richtigkeit.
  2. Corutin wird gestartet, das für die Bearbeitung dieser Anfrage verantwortlich ist. In jedem Sandkasten befinden sich viele Coroutinen in unterschiedlichen Zuständen.
  3. Die Anforderungsverarbeitung beginnt, was zwei Ergebnisse haben kann:
    • Verarbeitung erfolgreich abgeschlossen.
    • Corutin überträgt die Kontrolle auf den Server. Dies geschieht normalerweise, wenn Coroutine auf eine Antwort von anderen Diensten wartet. In solchen Fällen wird die Arbeit des Corutins ausgesetzt, bis eine Antwort eintrifft oder die Wartezeit abläuft. Beim Übertragen der Steuerung beginnt der Server mit der Verarbeitung der nächsten Anforderung. Dies kann entweder eine neue Anforderung oder eine Anforderung sein, die eine Antwort von allen beteiligten Diensten erhält und bereit ist, den Code weiter auszuführen. Die Reihenfolge der Verarbeitungsanforderungen wird durch die Anforderungen der Geschäftslogik bestimmt.


Die Verwendung von Corutin ist ein weiteres interessantes Thema, das eine ausführliche Diskussion verdient. In diesem Artikel wird beispielsweise erläutert, wie Corutins zum Erstellen von Zwischensequenzen in Videospielen verwendet werden können. Und die Coroutinen auf dem Anwendungsserver sollten einen separaten Artikel widmen, und vielleicht werden wir es in Zukunft tun.

Was weiter?


Und dann wird vielleicht die Verwendung von Lua in IPONWEB erweitert. Wir haben Ideen, wie Sie Lua noch in unserem Geschäft einsetzen können, und wenn diese Ideen umgesetzt werden, werden wir auf jeden Fall neue Erfahrungen teilen. Der Komfort und die Fähigkeiten von Lua als eingebettete Skriptsprache können uns insbesondere dabei helfen, die Verarbeitung von Kundendaten zu beschleunigen. Dies ist jedoch immer noch aus dem Bereich der Pläne und Perspektiven.

Zusammenfassend können wir sagen, dass sich unsere vor 11 Jahren getroffene Wahl der Sprache der Geschäftslogik weiterhin rechtfertigt, sodass wir unsere eigenen Geschäftsaufgaben erfolgreich bewältigen und uns bei der Lösung der Probleme unserer Kunden helfen können. Leicht zu lesen, leicht zu integrieren, schnell und leicht zu erlernen, war und ist Lua eine der besten Skriptsprachen, deren Umfang keineswegs auf die Spieleentwicklung beschränkt ist. Die darauf geschriebene IPONWEB-Geschäftslogik ist nur ein Beispiel dafür.

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


All Articles