
Wie bin ich zu diesem Leben gekommen?
Vor nicht allzu langer Zeit musste ich an dem Backend eines stark ausgelasteten Projekts arbeiten, in dem viele Hintergrundaufgaben mit komplexen Berechnungen und Anfragen an externe Dienste regelmĂ€Ăig ausgefĂŒhrt werden sollten. Das Projekt ist asynchron und bevor ich kam, gab es einen einfachen Mechanismus zum Auslösen von Aufgaben ĂŒber Cron: einen Zyklus, der die aktuelle Zeit ĂŒberprĂŒft und Gruppen von Koroutinen ĂŒber gather startet - dieses Vorgehen war akzeptabel, solange es nur Dutzende oder Hunderte von Koroutinen gab, aber als die Anzahl ĂŒber zweitausend stieg, musste ich darĂŒber nachdenken, wie man eine ordentliche Aufgabenwarteschlange mit einem Broker, mehreren Workern und mehr organisiert.
ZunĂ€chst beschloss ich, Celery auszuprobieren, das ich frĂŒher verwendet hatte. Aufgrund der AsynchronitĂ€t des Projekts tauchte ich in das Thema ein und sah , sowie , das vom Autor des Artikels erstellt wurde.
Ich kann sagen, das Projekt ist sehr interessant und funktioniert ziemlich gut in anderen Anwendungen unseres Teams, und auch der Autor selbst sagt, dass er es in Produktion bringen konnte, indem er einen asynchronen Pool verwendete. Leider war dies jedoch nicht ganz passend fĂŒr mich, da ich entdeckte bei der gruppenmĂ€Ăigen AusfĂŒhrung von Aufgaben (siehe ). Zum Zeitpunkt des Schreibens des Artikels bereits geschlossen, aber die Arbeit wurde einen Monat lang fortgesetzt. Auf jeden Fall wĂŒnsche ich dem Autor viel GlĂŒck und alles Gute, denn die funktionierenden Dinge in der Bibliothek sind bereits vorhanden⊠im Grunde genommen liegt das an mir und fĂŒr mich war das Werkzeug noch zu unreif. Zudem gab es in einigen Aufgaben 2-3 HTTP-Anfragen an verschiedene Dienste, so dass wir selbst bei Optimierung der Aufgaben etwa 4.000 TCP-Verbindungen alle 2 Stunden erstellen - nicht ideal... Ich hĂ€tte gerne bei der AusfĂŒhrung der Worker eine Sitzung fĂŒr einen Aufgabentyp erstellt. Ein wenig mehr ĂŒber die groĂe Anzahl von Anfragen ĂŒber aiohttp .
In diesem Zusammenhang begann ich nach Alternativen zu suchen und fand sie! Die Schöpfer von Celery, und zwar, wie ich verstand , schufen , ursprĂŒnglich fĂŒr das Projekt . Faust wurde inspiriert von Kafka Streams und arbeitet mit Kafka als Broker, auĂerdem wird fĂŒr die Speicherung der Ergebnisse der Agentenarbeit rocksdb verwendet, und das Wichtigste ist, dass die Bibliothek asynchron ist.
ZusĂ€tzlich können Sie sich das ansehen Celery und Faust von den Machern des Letzten: ihre Unterschiede, Unterschiede bei Brokern, die Implementierung einfacher Aufgaben. Alles ist ziemlich einfach, jedoch zieht bei Faust eine angenehme Besonderheit die Aufmerksamkeit auf sich â typisierte Daten zur Ăbertragung in ein Topic.
Was sollen wir tun?
In einer kleinen Artikelreihe werde ich zeigen, wie man Daten in Hintergrundaufgaben mit Faust sammelt. Die Quelle fĂŒr unser Beispielprojekt wird, wie aus dem Titel ersichtlich, . Ich werde demonstrieren, wie man Agenten (Sink, Topics, Partitionen) schreibt, regelmĂ€Ăige (Cron) AusfĂŒhrungen macht, die praktischsten CLI-Befehle von Faust (eine Wrapper ĂŒber Click), einfaches Clustering, und am Ende werden wir Datadog (der out-of-the-box funktioniert) einbinden und versuchen, etwas zu sehen. Zum Speichern der gesammelten Daten verwenden wir MongoDB und Motor zur Verbindung.
P.S. Angesichts der Zuversicht, mit der der Punkt ĂŒber Monitoring geschrieben ist, denke ich, dass der Leser am Ende des letzten Artikels so aussehen wird:

Anforderungen an das Projekt
Im Hinblick auf das, was ich bereits versprochen habe, lassen Sie uns eine kleine Liste dessen erstellen, was der Service können sollte:
- Wertpapiere und einen Ăberblick ĂŒber sie (einschlieĂlich Gewinne und Verluste, Bilanz, Cashflow â im letzten Jahr) regelmĂ€Ăig exportieren
- Historische Daten (fĂŒr jedes Handelsjahr Extremwerte der Schlusskurse finden) â regelmĂ€Ăig exportieren
- Aktuelle Handelsdaten exportieren â regelmĂ€Ăig
- Eine konfigurierbare Liste von Indikatoren fĂŒr jedes Wertpapier exportieren â regelmĂ€Ăig
Wie es sich gehört, wĂ€hlen wir einen Namen fĂŒr das Projekt willkĂŒrlich aus: Horton
Die Infrastruktur vorbereiten
Die Ăberschrift ist natĂŒrlich stark, aber alles, was zu tun ist â ist eine kleine Konfiguration fĂŒr docker-compose mit Kafka (und Zookeeper â in einem Container), Kafdrop (falls wir Nachrichten in den Topics sehen möchten), MongoDB schreiben. Wir erhalten [docker-compose.yml]() in folgender Art:
version: '3'
services:
db:
container_name: horton-mongodb-local
image: mongo:4.2-bionic
command: mongod --port 20017
restart: always
ports:
- 20017:20017
environment:
- MONGO_INITDB_DATABASE=horton
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=admin_password
kafka-service:
container_name: horton-kafka-local
image: obsidiandynamics/kafka
restart: always
ports:
- "2181:2181"
- "9092:9092"
environment:
KAFKA_LISTENERS: "INTERNAL://:29092,EXTERNAL://:9092"
KAFKA_ADVERTISED_LISTENERS: "INTERNAL://kafka-service:29092,EXTERNAL://localhost:9092"
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT"
KAFKA_INTER_BROKER_LISTENER_NAME: "INTERNAL"
KAFKA_ZOOKEEPER_SESSION_TIMEOUT: "6000"
KAFKA_RESTART_ATTEMPTS: "10"
KAFKA_RESTART_DELAY: "5"
ZOOKEEPER_AUTOPURGE_PURGE_INTERVAL: "0"
kafdrop:
container_name: horton-kafdrop-local
image: 'obsidiandynamics/kafdrop:latest'
restart: always
ports:
- '9000:9000'
environment:
KAFKA_BROKERCONNECT: kafka-service:29092
depends_on:
- kafka-serviceHier ist ĂŒberhaupt nichts kompliziert. FĂŒr Kafka wurden zwei Listener deklariert: einer (intern), um innerhalb des zusammengesetzten Netzwerks zu verwenden, und der zweite (extern), um Anfragen von auĂen zu bearbeiten, weshalb er nach auĂen weitergeleitet wurde. 2181 ist der Port des Zookeepers. FĂŒr den Rest denke ich, ist alles klar.
Wir bereiten das Skelett des Projekts vor
Im Grundsatz sollte die Struktur unseres Projekts wie folgt aussehen:
horton
âââ docker-compose.yml
âââ horton
âââ agents.py *
âââ alphavantage.py *
âââ app.py *
âââ config.py
âââ database
â  âââ connect.py
â  âââ cruds
â  â  âââ base.py
â  â  âââ __init__.py
â  â  âââ security.py *
â  âââ __init__.py
âââ __init__.py
âââ records.py *
âââ tasks.py **Alles, was ich markiert habe berĂŒhren wir vorerst nicht, sondern erstellen einfach leere Dateien.**
Wir haben die Struktur erstellt. Jetzt fĂŒgen wir die notwendigen AbhĂ€ngigkeiten hinzu, schreiben die Konfiguration und die Verbindung zu MongoDB. Den vollstĂ€ndigen Text der Dateien werde ich im Artikel nicht wiedergeben, um es nicht zu lang zu machen, sondern mache stattdessen Links zu den benötigten Versionen.
Lass uns mit den AbhÀngigkeiten und den Metadaten zum Projekt beginnen -
Dann starten wir die Installation der AbhÀngigkeiten und die Erstellung des Virtualenv (oder, alternativ könnt ihr den Ordner venv selbst erstellen und die Umgebung aktivieren):
pip3 install poetry (wenn noch nicht installiert)
poetry installJetzt erstellen wir â die Anmeldedaten und den Verbindungsort. Dort können wir auch die Daten fĂŒr Alphavantage hinterlegen. Und jetzt gehen wir zu â wir extrahieren die Daten fĂŒr die Anwendung aus unserer Konfiguration. Ja, ich gebe es zu, ich habe meine eigene Bibliothek verwendet â .
Die Verbindung mit MongoDB ist ganz einfach. Wir haben erklĂ€rt zur Verbindung und fĂŒr die CRUD-Operationen, um die Anfragen an die Sammlungen zu erleichtern.
Was wird als NĂ€chstes passieren?
Der Artikel ist nicht sehr lang geworden, da ich hier nur ĂŒber Motivation und Vorbereitung spreche. Also bitte verzeihe mir â ich verspreche, dass es im nĂ€chsten Teil Action und Grafiken geben wird.
Also, in diesem nÀchsten Teil werden wir:
- Einen kleinen Client fĂŒr alphavantage mit aiohttp schreiben, der Anfragen an die benötigten Endpunkte sendet.
- Einen Agenten erstellen, der Daten ĂŒber Wertpapiere und deren historische Preise sammelt.
Quelle: habr.com
