
Im Jahr 2017 gewannen wir den Wettbewerb zur Entwicklung des Transaktionskerns für das Investmentgeschäft der Alfa-Bank und begannen mit der Arbeit (auf HighLoad++ 2018 mit einem Vortrag über den Investmentgeschäftskern Wladimir Dryinkin, Leiter des Transaktionskern-Managements der Alfa-Bank). Dieses System sollte Daten zu Transaktionen aus verschiedenen Quellen in unterschiedlichen Formaten aggregieren, die Daten in ein einheitliches Format bringen, sie speichern und den Zugriff darauf ermöglichen.
Im Verlauf der Entwicklung hat sich das System weiterentwickelt und zusätzliche Funktionen erhalten, und irgendwann wurde uns klar, dass sich hier etwas Größeres kristallisiert als nur eine Anwendungssoftware, die für einen klar definierten Aufgabenbereich geschaffen wurde: Wir hatten eine Plattform zur Erstellung verteilter Anwendungen mit persistentem Speicher. Die von uns gesammelten Erfahrungen bildeten die Grundlage für ein neues Produkt — (TDG).
Ich möchte Ihnen die Architektur von TDG und die Lösungen vorstellen, die wir während der Entwicklung gefunden haben. Zudem möchte ich Ihnen die Hauptfunktionen näherbringen und zeigen, wie unser Produkt die Basis für vollständige Lösungen bilden kann.
Architektonisch haben wir das System in einzelne Rollen, die jeweils für die Lösung bestimmter Aufgaben verantwortlich sind. Eine gestartete Instanz der Anwendung implementiert eine oder mehrere Rollen. In einem Cluster kann es mehrere Rollen desselben Typs geben:

Connector
Der Connector sorgt für die Verbindung zur Außenwelt; seine Aufgabe ist es, Anfragen entgegenzunehmen, sie zu analysieren, und wenn dies erfolgreich ist, die Daten zur Verarbeitung an den Input Processor zu senden. Wir unterstützen die Formate HTTP, SOAP, Kafka, FIX. Die Architektur ermöglicht es, die Unterstützung neuer Formate problemlos hinzuzufügen; bald wird auch IBM MQ unterstützt. Wenn das Parsing der Anfrage fehlschlägt, gibt der Connector einen Fehler zurück; andernfalls bestätigt er, dass die Anfrage erfolgreich verarbeitet wurde, auch wenn ein Fehler bei der weiteren Verarbeitung aufgetreten ist. Dies wurde speziell eingerichtet, um mit Systemen zu arbeiten, die Anfragen nicht erneut senden können — oder vice versa, dies zu nachdrücklich tun. Um Datenverluste zu vermeiden, wird eine Reparaturwarteschlange verwendet: Das Objekt gelangt zuerst in diese und wird erst nach erfolgreicher Verarbeitung daraus entfernt. Der Administrator kann Benachrichtigungen über Objekte, die in der Reparaturwarteschlange verbleiben, erhalten und nach Behebung der Software- oder Hardwarefehler einen neuen Versuch unternehmen.
Input processor
Der Eingangsprozessor klassifiziert die empfangenen Daten nach charakteristischen Merkmalen und ruft die entsprechenden Handler auf. Die Handler sind Code in Lua, der in einer Sandbox ausgeführt wird, wodurch sie das Funktionieren des Systems nicht beeinflussen können. In dieser Phase können die Daten in das erforderliche Format gebracht werden, und bei Bedarf kann eine beliebige Anzahl von Aufgaben gestartet werden, die die notwendige Logik umsetzen. Zum Beispiel wird in dem MDM-Produkt (Master Data Management), das auf Tarantool Data Grid basiert, bei der Hinzufügung eines neuen Benutzers, um die Verarbeitung des Antrags nicht zu verlangsamen, die Erstellung des Goldenen Datensatzes in einer separaten Aufgabe gestartet. Die Sandbox unterstützt Lese-, Änderungs- und Hinzufügungsanfragen von Daten und ermöglicht die Ausführung einer Funktion für alle Arten von Rollen wie Storage sowie die Aggregation des Ergebnisses (map/reduce).
Handler können in Dateien beschrieben werden:
sum.lua
local x, y = unpack(...)
return x + yUnd dann in der Konfiguration deklariert werden:
functions:
sum: { __file: sum.lua }
Warum Lua? Lua ist eine sehr einfache Sprache. Nach unserer Erfahrung beginnen die Leute, nach nur wenigen Stunden mit ihr Code zu schreiben, der ihre Probleme löst. Und das sind nicht nur professionelle Entwickler, sondern auch Analysten. Außerdem arbeitet Lua dank des JIT-Compilers sehr schnell.
Speicher
Der Speicher hält persistent Daten. Vor der Speicherung werden die Daten auf Übereinstimmung mit dem Datenschema validiert. Für die Beschreibung des Schemas verwenden wir ein erweitertes Format. . Beispiel:
{
"name": "User",
"type": "record",
"logicalType": "Aggregate",
"fields": [
{ "name": "id", "type": "string"},
{"name": "first_name", "type": "string"},
{"name": "last_name", "type": "string"}
],
"indexes": ["id"]
}Aus dieser Beschreibung wird automatisch DDL (Data Definition Language) für die Datenbank Taranthul generiert und ein Schema für den Datenzugriff.
Es wird asynchrone Datenreplikation unterstützt (in Planung ist die Implementierung einer synchronen Replikation).
Ausgabeprozessor
Manchmal müssen externe Verbraucher über die Bereitstellung neuer Daten informiert werden. Dazu gibt es die Rolle des Output-Prozessors. Nach der Speicherung der Daten können sie an den entsprechenden Handler übergeben werden (zum Beispiel, um sie in das gewünschte Format für den Verbraucher zu bringen) — und anschließend an den Connector zur Versendung weitergeleitet werden. Hier wird ebenfalls eine Reparaturwarteschlange verwendet: Wenn kein Objekt angenommen wurde, kann der Administrator später einen erneuten Versuch starten.
Skalierung
Die Rollen des Connectors, Input-Prozessors und Output-Prozessors haben keinen Status, was es uns ermöglicht, das System horizontal zu skalieren, indem einfach neue Instanzen der Anwendung mit aktivierter Rolle des benötigten Typs hinzugefügt werden. Für die horizontale Skalierung wird Storage genutzt. zur Organisation des Clusters unter Verwendung virtueller Buckets. Nach der Hinzufügung eines neuen Servers werden Teile der Buckets von den alten Servern im Hintergrund auf den neuen Server verschoben; dies geschieht transparent für die Benutzer und hat keine Auswirkungen auf den Betrieb des gesamten Systems.
Datenmerkmale
Objekte können sehr groß sein und andere Objekte enthalten. Wir gewährleisten die Atomarität beim Hinzufügen und Aktualisieren von Daten, indem wir das Objekt mit allen Abhängigkeiten in einem virtuellen Bucket speichern. Dadurch wird das "Verteilen" des Objekts auf mehrere physische Server ausgeschlossen.
Versionsverwaltung wird unterstützt: Jedes Update eines Objekts erstellt eine neue Version, und wir können jederzeit einen zeitlichen Schnitt machen und sehen, wie die Welt damals aussah. Für Daten, die keine lange Historie benötigen, können wir die Anzahl der Versionen beschränken oder sogar nur eine - die letzte - aufbewahren, was bedeutet, dass wir die Versionsverwaltung für einen bestimmten Typ faktisch deaktivieren können. Außerdem können wir die Historie zeitlich begrenzen: Zum Beispiel könnten wir alle Objekte eines bestimmten Typs, die älter als ein Jahr sind, löschen. Auch die Archivierung wird unterstützt: Wir können Objekte, die älter als eine bestimmte Zeit sind, exportieren, um Platz im Cluster freizugeben.
Aufgaben
Unter den interessanten Funktionen ist die Möglichkeit, Aufgaben nach Zeitplan, auf Benutzeranfrage oder programmatisch aus der Sandbox zu starten, hervorzuheben:

Hier sehen wir eine weitere Rolle – Runner. Diese Rolle hat keinen Zustand, und bei Bedarf können zusätzliche Instanzen der Anwendung mit dieser Rolle zum Cluster hinzugefügt werden. Die Verantwortung des Runners besteht darin, Aufgaben auszuführen. Wie bereits erwähnt, ist es aus der Sandbox möglich, neue Aufgaben zu generieren; diese werden in der Warteschlange im Storage gespeichert und später auf dem Runner ausgeführt. Diese Art von Aufgaben wird Job genannt. Außerdem haben wir eine Art von Aufgaben, die Task genannt wird – das sind benutzerdefinierte Aufgaben, die nach Zeitplan (unter Verwendung der Cron-Syntax) oder auf Anfrage ausgeführt werden. Um solche Aufgaben zu starten und zu überwachen, haben wir einen benutzerfreundlichen Task-Manager. Damit diese Funktionalität verfügbar ist, muss die Rolle Scheduler aktiviert werden; diese Rolle hat einen Zustand, weshalb sie nicht skaliert werden kann, was jedoch auch nicht erforderlich ist; sie kann, wie alle anderen Rollen, eine Replik haben, die aktiv wird, wenn der Master ausfällt.
Logger
Eine weitere Rolle wird Logger genannt. Sie sammelt Protokolle von allen Mitgliedern des Clusters und bietet eine Schnittstelle zum Export und zur Anzeige über die Web-Oberfläche.
Dienste
Es ist erwähnenswert, dass das System das einfache Erstellen von Services ermöglicht. In der Konfigurationsdatei kann angegeben werden, welche Anfragen an den benutzerdefinierten Handler weitergeleitet werden, der in einer Sandbox ausgeführt wird. In diesem Handler können beispielsweise analytische Anfragen durchgeführt und die Ergebnisse zurückgegeben werden.
Der Service wird in der Konfigurationsdatei beschrieben:
services:
sum:
doc: "addiert zwei Zahlen"
function: sum
return_type: int
args:
x: int
y: int
Die GraphQL-API wird automatisch generiert und der Service wird zur Aufrufbereitstellung verfügbar:
query {
sum(x: 1, y: 2)
} Dies führt zur Ausführung des Handlers sum, der das Ergebnis zurückgibt:
3
Anfrageprofilierung und Metriken
Um das System zu verstehen und Anfragen zu profilieren, haben wir die Unterstützung des OpenTracing-Protokolls implementiert. Das System kann auf Anforderung Informationen an Werkzeuge senden, die dieses Protokoll unterstützen, wie z.B. Zipkin, was hilft zu verstehen, wie die Anfrage ausgeführt wurde:

Natürlich stellt das System interne Metriken zur Verfügung, die mit Prometheus gesammelt und mit Grafana visualisiert werden können.
Deployment
Tarantool Data Grid kann aus RPM-Paketen oder Archiven bereitgestellt werden, entweder über das mitgelieferte Tool oder Ansible, und es gibt auch Unterstützung für Kubernetes ().
Die Anwendung, die die Geschäftslogik implementiert (Konfiguration, Handler), wird in den bereitgestellten Tarantool Data Grid-Cluster als Archiv über die Benutzeroberfläche oder mit einem Skript und über unsere API geladen.
Beispiele für Anwendungen
Welche Anwendungen lassen sich mit Tarantool Data Grid erstellen? Tatsächlich hängen die meisten Geschäftsaufgaben in irgendeiner Form mit der Verarbeitung von Datenströmen sowie deren Speicherung und Zugriff zusammen. Wenn Sie also große Datenströme haben, die zuverlässig gespeichert werden müssen und auf die Sie zugreifen möchten, kann unser Produkt Ihnen viel Entwicklungszeit sparen und Ihnen ermöglichen, sich auf Ihre Geschäftslogik zu konzentrieren.
Zum Beispiel möchten wir Informationen über den Immobilienmarkt sammeln, um später Informationen über die besten Angebote zu haben. In diesem Fall können wir die folgenden Aufgaben festlegen:
- Roboter, die Informationen aus offenen Quellen sammeln - das werden unsere Datenquellen sein. Diese Aufgabe können Sie lösen, indem Sie fertige Lösungen verwenden oder Code in einer beliebigen Programmiersprache schreiben.
- Anschließend wird Tarantool Data Grid die Daten empfangen und speichern. Wenn die Datenformate aus verschiedenen Quellen abweichen, können Sie Code in Lua schreiben, der eine einheitliche Formatierung vornimmt. In der Vorverarbeitungsphase können Sie zum Beispiel doppelte Sätze filtern oder zusätzliche Informationen über die auf dem Markt tätigen Agenten in der Datenbank aktualisieren.
- Jetzt haben Sie bereits eine skalierbare Lösung im Cluster, die mit Daten gefüllt werden kann und Datenabfragen ermöglicht. Danach können Sie neue Funktionen implementieren, wie z. B. einen Dienst zu schreiben, der eine Anfrage an die Daten richtet und das vorteilhafteste Angebot innerhalb eines Tages herausgibt – dies erfordert nur einige Zeilen in der Konfigurationsdatei und etwas Lua-Code.
Wie geht es weiter?
Bei uns hat die Verbesserung der Entwicklungsfreundlichkeit höchste Priorität durch . Zum Beispiel eine IDE mit Unterstützung für Profiling und Debugging von Handlern, die in einer Sandbox ausgeführt werden.
Wir legen ebenfalls großen Wert auf Sicherheitsfragen. Momentan durchlaufen wir die Zertifizierung durch die FSTEC Russlands, um ein hohes Sicherheitsniveau zu bestätigen und den Anforderungen an die Zertifizierung von Softwareprodukten, die in Informationssystemen für personenbezogene Daten und staatlichen Informationssystemen verwendet werden, zu entsprechen.
Quelle: habr.com
