Baggrundsopgaver pÄ Faust, Del I: Introduktion

Baggrundsopgaver pÄ Faust, Del I: Introduktion

Hvordan kom jeg til dette liv?

For nylig arbejdede jeg pĂ„ backend’en af et hĂžjt belastet projekt, hvor jeg skulle organisere regelmĂŠssig udfĂžrelse af et stort antal baggrundsopgaver med komplekse beregninger og forespĂžrgsler til tredjepartsservices. Projektet er asynkront, og inden jeg kom ind i det, var der en simpel mekanisme til planlĂŠgning af opgaver: en lĂžkke der kontrollerede den nuvĂŠrende tid og startede grupper af korutiner gennem gather — denne tilgang viste sig at vĂŠre acceptabel, indtil antallet af korutiner blev titusinder, men da antallet oversteg to tusinde, var vi nĂždt til at overveje at organisere en normal opgavekĂž med en broker, flere workere og lignende.

FÞrst besluttede jeg at prÞve Celery, som jeg tidligere havde brugt. PÄ grund af projektets asynkronitet dykkede jeg ind i spÞrgsmÄlet og sÄ artiklen, samt projekt, skabt af forfatteren af artiklen.

Jeg vil sige, at projektet er meget interessant og fungerer ganske godt i andre applikationer i vores team, og forfatteren selv nĂŠvner, at han har vĂŠret i stand til at implementere det i produktion ved at bruge en asynkron pulje. Men desvĂŠrre var det ikke helt egnet til mig, da der opstod problemet vedrĂžrende gruppering af opgaveudfĂžrelser (se gruppe). PĂ„ tidspunktet for skrivningen af artiklen issue var allerede afsluttet, men arbejdet blev udfĂžrt i lĂžbet af en mĂ„ned. Uanset hvad, Ăžnsker jeg forfatteren held og lykke, da der allerede er fungerende lĂžsninger i biblioteket... Kort sagt, det er mit problem, og for mig var vĂŠrktĂžjet lidt for ufuldstĂŠndigt. Desuden var der i nogle opgaver 2-3 http-forespĂžrgsler til forskellige tjenester, sĂ„ledes at selv ved optimering af opgaverne skaber vi 4.000 tcp-forbindelser cirka hver 2. time — ikke ideelt... Jeg ville gerne have en session pr. opgavetype ved start af workere. Mere om det store antal forespĂžrgsler gennem aiohttp her.

Fordi dette begyndte jeg at sĂžge alternativer og fandt! Skaberne af Celery, mere prĂŠcist, som jeg forstod Ask Solem, havde skabt Faust, oprindeligt til projektet robinhood. Faust er inspireret af Kafka Streams og arbejder med Kafka som broker, ligeledes anvendes RocksDB til lagring af resultaterne fra arbejdet med agenter, og det vigtigste er, at biblioteket er asynkront.

Du kan ogsĂ„ se en kort sammenligning celery og faust fra skaberne af den seneste: deres forskelle, forskelle mellem mĂŠglere, implementeringen af en simpel opgave. Det er alt sammen ret enkelt, men faust tiltrĂŠkker opmĂŠrksomhed med en behagelig egenskab — typesikrede data til overfĂžrsel til emner.

Hvad skal vi gĂžre?

SĂ„ i en lille serie artikler vil jeg vise, hvordan man indsamler data i baggrundsopgaver med hjĂŠlp fra Faust. Kilden til vores eksempelprojekt vil, som titlen antyder, vĂŠre alphavantage.co. Jeg vil demonstrere, hvordan man skriver agenter (sinks, emner, partitioner), hvordan man udfĂžrer regelmĂŠssige (cron) kĂžringer, de mest praktiske cli-kommandoer i faust (en wrapper over click), simpel clustering, og til sidst vil vi integrere datadog (som fungerer ud af boksen) og forsĂžge at se noget. Til opbevaring af de indsamlede data vil vi bruge mongodb og motor til at forbinde.

P.S. I betragtning af den selvsikkerhed, der er skrevet i afsnittet om overvÄgning, tror jeg, at lÊseren i slutningen af den sidste artikel vil se sÄdan ud:

Baggrundsopgaver pÄ Faust, Del I: Introduktion

Krav til projektet

Da jeg allerede har lovet en del, lad os lave en lille liste over, hvad tjenesten skal kunne:

  1. Hente vĂŠrdipapirer og en oversigt over dem (herunder indtĂŠgter og udgifter, balance, cash flow — for det seneste Ă„r) — regelmĂŠssigt
  2. Hente historiske data (for hvert handelsĂ„r finde ekstrempriserne for afslutningskurserne) — regelmĂŠssigt
  3. Hente de seneste handelsdata — regelméssigt
  4. Hente en konfigureret liste over indikatorer for hvert vérdipapir — regelméssigt

Som sÊdvanligt vÊlger vi et navn til projektet fra det blÄ: horton

Forberede infrastrukturen

Titlen er bestemt stĂŠrk, men alt hvad der skal gĂžres er at skrive en lille konfiguration for docker-compose med kafka (og zookeeper — i en enkelt container), kafdrop (hvis vi vil se beskeder i emnerne), mongodb. Vi fĂ„r [docker-compose.yml](https://github.com/Egnod/horton/blob/562fa5ec14df952cd74760acf76e141707d2ef58/docker-compose.yml) af fĂžlgende slags:

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

Der er faktisk ikke noget kompliceret her. For Kafka har vi erklÊret to lyttere: en (intern) til brug inden for det samlede netvÊrk og den anden (extern) til forespÞrgsler udefra, derfor har vi Äbnet den udadtil. 2181 er zookeeper-porten. Resten, tÊnker jeg, er klart.

Vi forbereder projektets skelet

I den grundlÊggende version skal strukturen af vores projekt se sÄledes ud:

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 *

*Alt det, jeg har markeret rĂžrer vi ikke ved endnu, men laver blot tomme filer.

Vi har oprettet strukturen. Nu tilfÞjer vi de nÞdvendige afhÊngigheder, skriver konfigurationen og forbindelsen til MongoDB. Jeg vil ikke medtage hele teksten af filerne i artiklen for ikke at gÞre den for lang, sÄ jeg vil lave links til de nÞdvendige versioner.

Lad os begynde med afhéngighederne og metaoplysningerne om projektet — pyproject.toml

Derefter starter vi installationen af afhĂŠngighederne og oprettelsen af et virtualenv (eller, du kan selv oprette en mappe venv og aktivere miljĂžet):

pip3 install poetry (hvis ikke allerede installeret)
poetry install

Lad os nu oprette config.yml — credentialer og hvor vi skal kommunikere. Her kan vi ogsĂ„ placere oplysninger til alphavantage. Og sĂ„ gĂ„r vi videre til config.py — vi henter data til applikationen fra vores konfiguration. Ja, jeg beklager, jeg har brugt mit eget bibliotek — sitri.

Forbindelsen med MongoDB er heller ikke besvÊrlig. Vi har erklÊret en klientklasse til forbindelsen og en grundlÊggende klasse til CRUD-operationer, for at gÞre det lettere at udfÞre forespÞrgsler pÄ samlingerne.

Hvad vil der ske videre?

Artiklen er ikke sÊrlig lang, da jeg kun taler om motivation og forberedelse her, sÄ bÊr over med mig - jeg lover, at der i nÊste del vil vÊre action og grafik.

SĂ„ i denne nĂŠste del skal vi:

  1. Skrive en lille klient til alphavantage pÄ aiohttp med forespÞrgsler til de endepunkter, vi har brug for.
  2. Lave en agent, der indsamler data om vÊrdipapirer og historiske priser pÄ dem.

Projektkode

Koden til denne del

Kilde: habr.com

KĂžb pĂ„lidelig webhosting med DDoS-beskyttelse, VPS VDS-servere đŸ”„ KĂžb pĂ„lidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster