sql >> Database teknologi >  >> RDS >> Database

Asynkrone opgaver med Django og selleri

Da jeg var ny til Django, var en af ​​de mest frustrerende ting, jeg oplevede, behovet for at køre lidt kode med jævne mellemrum. Jeg skrev en fin funktion, der udførte en handling, der skulle køre dagligt kl. 12.00. Nemt, ikke? Forkert. Dette viste sig at være et stort problem for mig, da jeg på det tidspunkt var vant til "Cpanel-type" webhosting, hvor der var en fin praktisk GUI til at opsætte cron-job til netop dette formål.

Efter megen research fandt jeg en god løsning – Selleri, en kraftfuld asynkron jobkø, der bruges til at køre opgaver i baggrunden. Men dette førte til yderligere problemer, da jeg ikke kunne finde et let sæt instruktioner til at integrere Selleri i et Django-projekt.

Selvfølgelig lykkedes det mig til sidst at finde ud af det - hvilket er, hvad denne artikel vil dække:Sådan integreres selleri i et Django-projekt og opretter periodiske opgaver.

Gratis bonus: Klik her for at få adgang til en gratis Django Learning Resources Guide (PDF), der viser dig tips og tricks samt almindelige faldgruber, du skal undgå, når du bygger Python + Django-webapplikationer.

Dette projekt bruger Python 3.4, Django 1.8.2, Celery 3.1.18 og Redis 3.0.2.


Oversigt

For nemheds skyld, da dette er så stort et indlæg, bedes du henvise tilbage til denne tabel for kort info om hvert trin og for at få fat i den tilhørende kode.

Trin Oversigt Git Tag
Boilerplate Download boilerplate v1
Opsætning Integrer selleri med Django v2
Selleriopgaver Tilføj grundlæggende selleriopgave v3
Periodiske opgaver Tilføj periodisk opgave v4
Kører lokalt Kør vores app lokalt v5
Kører eksternt Kør vores app eksternt v6


Hvad er selleri?

"Selleri er en asynkron opgavekø/jobkø baseret på distribueret beskedoverførsel. Det er fokuseret på drift i realtid, men understøtter også planlægning." Til dette indlæg vil vi fokusere på planlægningsfunktionen til periodisk at køre et job/en opgave.

Hvorfor er dette nyttigt?

  • Tænk på alle de gange, du har skullet køre en bestemt opgave i fremtiden. Måske skulle du have adgang til en API hver time. Eller måske havde du brug for at sende en masse e-mails sidst på dagen. Stor eller lille, Celery gør planlægning af sådanne periodiske opgaver let.
  • Du ønsker aldrig, at slutbrugere skal vente unødvendigt på, at siderne indlæses, eller på, at handlinger udføres. Hvis en lang proces er en del af din applikations arbejdsgang, kan du bruge Celery til at udføre denne proces i baggrunden, efterhånden som ressourcer bliver tilgængelige, så din applikation kan fortsætte med at svare på klientanmodninger. Dette holder opgaven ude af applikationens kontekst.


Opsætning

Inden du dykker ned i Selleri, tag fat i startprojektet fra Github-reposen. Sørg for at aktivere en virtualenv, installere kravene og køre migreringerne. Tænd derefter serveren og naviger til http://localhost:8000/ i din browser. Du bør se den velkendte tekst "Tillykke med din første Django-drevne side". Når du er færdig, dræb serveren.

Lad os derefter installere Selleri ved hjælp af pip:

$ pip install celery==3.1.18$ pip freeze> requirements.txt 

Nu kan vi integrere selleri i vores Django-projekt med kun tre nemme trin.


Trin 1:Tilføj celery.py

Inde i "picha"-mappen skal du oprette en ny fil kaldet celery.py :

fra __future__ import absolute_importimport osfra selleri import Celeryfrom django.conf import settings# indstil standard Django indstillingsmodulet for 'selleri' programmet.os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'picha.settings')appen =Selleri('picha')# Brug af en streng her betyder, at arbejderen ikke skal # pickle objektet, når han bruger Windows.app.config_from_object('django.conf:settings')app.autodiscover_tasks(lambda:settings.INSTALLED_APPS)@ app.task(bind=True)def debug_task(self):print('Request:{0!r}'.format(self.request)) 

Bemærk kommentarerne i koden.



Trin 2:Importer din nye Selleri-app

For at sikre, at Celery-appen er indlæst, når Django starter, skal du tilføje følgende kode i __init__.py fil, der sidder ved siden af ​​din settings.py fil:

fra __future__ import absolute_import# Dette vil sikre, at appen altid importeres, når# Django starter, så shared_task vil bruge denne app.from .celery import app som celery_app 

Når du har gjort det, skulle dit projektlayout nu se sådan ud:

├── manage.py├── picha│ ├── __init__.py│ ├── celery.py│ ├── settings.py│ ├──s. py└── requirements.txt 


Trin 3:Installer Redis som en selleri-"mægler"

Selleri bruger "mæglere" til at videregive beskeder mellem et Django-projekt og Selleri-arbejderne. I denne vejledning vil vi bruge Redis som meddelelsesmægler.

Først skal du installere Redis fra den officielle downloadside eller via brew (brew install redis ) og vend derefter til din terminal i et nyt terminalvindue, tænd serveren:

$ redis-server 

Du kan teste, at Redis fungerer korrekt ved at skrive dette i din terminal:

$ redis-cli ping 

Redis skal svare med PONG - prøv det!

Når Redis er klar, skal du tilføje følgende kode til filen settings.py:

# CELLERY STUFFBROKER_URL ='redis://localhost:6379'CELLERY_RESULT_BACKEND ='redis://localhost:6379'CELERY_ACCEPT_CONTENT =['application/json']CELERY_TASK_SERIALIZER ='json'CELERY_SERIALIZERCULTRY_JSON'CELERY' ='Afrika/Nairobi' 

Du skal også tilføje Redis som en afhængighed i Django-projektet:

$ pip install redis==2.10.3$ pip freeze> requirements.txt 

Det er det! Du skulle nu kunne bruge Selleri med Django. For mere information om opsætning af Selleri med Django, se venligst den officielle Selleri-dokumentation.

Før vi går videre, lad os køre et par fornuftstjek for at sikre, at alt er i orden...

Test, at Selleri-medarbejderen er klar til at modtage opgaver:

$ selleri -A picha worker -l info...[2015-07-07 14:07:07,398:INFO/MainProcess] Forbundet til redis://localhost:6379//[2015-07- 07 14:07:07,410:INFO/MainProcess] mingle:søger efter naboer[2015-07-07 14:07:08,419:INFO/MainProcess] mingle:helt alene 

Dræb processen med CTRL-C. Test nu, at Selleri-opgaveplanlægningen er klar til handling:

$ selleri -A picha beat -l info...[2015-07-07 14:08:23,054:INFO/MainProcess] beat:Starter... 

Bom!

Igen, dræb processen, når du er færdig.




Selleriopgaver

Selleri bruger opgaver, som kan opfattes som almindelige Python-funktioner, der kaldes med Selleri.

Lad os for eksempel omdanne denne grundlæggende funktion til en selleri-opgave:

def add(x, y):returner x + y 

Tilføj først en dekoratør:

fra celery.decorators import task@task(name="sum_two_numbers")def add(x, y):returner x + y 

Så kan du køre denne opgave asynkront med Selleri som sådan:

add.delay(7, 8) 

Simpelt, ikke?

Så disse typer opgaver er perfekte, når du vil indlæse en webside uden at få brugeren til at vente på, at en baggrundsproces er fuldført.

Lad os se på et eksempel...

Gå tilbage til Django-projektet, tag version tre, som inkluderer en app, der accepterer feedback fra brugere, passende kaldet feedback :

├── feedback│ ├── __init__.py│ ├── admin.py│ ├── emails.py│ ├── forms.py│─├s.py│─├s. .py│ └── views.py├── administrere.py├── picha│ ├── __init__.py│ ├── selleri.py│ ├─│ ├─│ls indstillinger.py. ─ wsgi.py├── requirements.txt└── skabeloner ├── base.html └── feedback ├── contact.html └── e-mail ├── mail ─── feedback _email_body.txt 

Installer de nye krav, tænd appen, og naviger til http://localhost:8000/feedback/. Du skal se:

Lad os opbygge Selleri-opgaven.


Tilføj opgaven

Grundlæggende vil vi, efter at brugeren har indsendt feedbackformularen, straks lade ham fortsætte på sin glade måde, mens vi behandler feedbacken, sender en e-mail osv., alt sammen i baggrunden.

For at opnå dette skal du først tilføje en fil kaldet tasks.py til "feedback"-biblioteket:

fra celery.decorators importer opgave fra celery.utils.log importer get_task_loggerffra feedback.emails import send_feedback_emaillogger =get_task_logger(__name__)@task(name="send_feedback_email_task")def send_feedback_email"end_task):"("e-mailend_opgave):en e-mail, når feedbackformularen er udfyldt""" logger.info("Sendt feedback-e-mail") return send_feedback_email(e-mail, besked) 

Opdater derefter forms.py sådan:

fra django import formularer fra feedback.tasks import send_feedback_email_taskclass FeedbackForm(forms.Form):email =forms.EmailField(label="Email Address") message =forms.CharField( label="Message", widget=forms .Textarea(attrs={'rows':5})) honeypot =forms.CharField(widget=forms.HiddenInput(), required=False) def send_email(self):# prøv at narre spammere ved at kontrollere, om honeypot-feltet er # udfyldt; ikke super kompliceret/effektivt, men det virker, hvis self.cleaned_data['honeypot']:returner False send_feedback_email_task.delay( self.cleaned_data['email'], self.cleaned_data['message']) 

Kort sagt, send_feedback_email_task.delay(e-mail, besked) funktion behandler og sender feedback-e-mailen i baggrunden, mens brugeren fortsætter med at bruge siden.

BEMÆRK :success_url i views.py er indstillet til at omdirigere brugeren til / , som ikke eksisterer endnu. Vi sætter dette slutpunkt op i næste afsnit.




Periodiske opgaver

Ofte bliver du nødt til at planlægge en opgave, der skal køre på et bestemt tidspunkt hver anden gang - det vil sige, at en webskraber muligvis skal køre dagligt, for eksempel. Sådanne opgaver, kaldet periodiske opgaver, er nemme at sætte op med Selleri.

Selleri bruger "selleri beat" til at planlægge periodiske opgaver. Celery beat kører opgaver med jævne mellemrum, som derefter udføres af selleriarbejdere.

For eksempel er følgende opgave planlagt til at køre hvert kvarter:

fra celery.task.schedules import crontab fra celery.decorators import periodic_task@periodic_task(run_every=(crontab(minute='*/15')), name="some_task", ignore_result=True)def some_task( ):# gør noget 

Lad os se på et mere robust eksempel ved at tilføje denne funktionalitet i Django-projektet...

Tilbage til Django-projektet, tag version fire, som inkluderer en anden ny app, kaldet photos , der bruger Flickr API til at få nye billeder til visning på webstedet:

├── feedback│ ├── __init__.py│ ├── admin.py│ ├── emails.py│ ├── forms.py│─├s.py│─├s.py│─├s. .py- ─ settings.py│ ├── tests.py│ ├── utils.py│ └── views.py├── picha│ ├── __init__.py│ ├py│ ├py│ ├ ├ │ ├ ├ ├ ├ │ ├── urls.py│ └── wsgi.py├── requirements.txt└── skabeloner ├── base.html ├── └ │── │─── │─│ kontakt.html. feedback_email_body.txt │ └── feedback_email_subject.txt └── billeder └── photo_list.html 

Installer de nye krav, kør migreringerne, og tænd derefter serveren for at sikre, at alt er godt. Prøv at teste feedbackformularen igen. Denne gang skulle den omdirigere fint.

Hvad er det næste?

Da vi med jævne mellemrum skulle kalde Flickr API for at tilføje flere billeder til vores websted, kan vi tilføje en Selleri-opgave.


Tilføj opgaven

Tilføj en tasks.py til billederne app:

fra celery.task.schedules import crontab fra celery.decorators import periodic_task fra celery.utils.log import get_task_loggerffra photos.utils import save_latest_flickr_imagelogger =get_task_logger(__name__)@periodic_task='cron_15='cron_15='cron_15=')), name="task_save_latest_flickr_image", ignore_result=True)def task_save_latest_flickr_image():""" Gemmer seneste billede fra Flickr """ save_latest_flickr_image() logger.info("Gemt billede fra Flickr") 

Her kører vi save_latest_flickr_image() funktion hvert 15. minut ved at pakke funktionsopkaldet ind i en opgave . @periodic_task decorator abstraherer koden for at køre Selleri-opgaven og forlader tasks.py fil ren og let at læse!




Kører lokalt

Klar til at køre denne ting?

Med din Django-app og Redis kørende, skal du åbne to nye terminalvinduer/faner. I hvert nyt vindue skal du navigere til din projektmappe, aktivere din virtualenv og derefter køre følgende kommandoer (en i hvert vindue):

$ selleri -A picha worker -l info$ sellery -A picha beat -l info 

Når du besøger webstedet på http://127.0.0.1:8000/ skulle du nu se ét billede. Vores app får et billede fra Flickr hvert 15. minut:

Tag et kig på photos/tasks.py for at se koden. Ved at klikke på knappen "Feedback" kan du... sende feedback:

Dette fungerer via en selleriopgave. Tag et kig på feedback/tasks.py for mere.

Det er det, du har Picha-projektet i gang!

Dette er godt til at teste, mens du udvikler dit Django-projekt lokalt, men fungerer ikke så godt, når du skal implementere til produktion - som på DigitalOcean, måske. Til det anbefales det, at du kører Celery-arbejderen og skemalæggeren i baggrunden som en dæmon med Supervisor.



Løber eksternt

Installationen er enkel. Få fat i version fem fra repoen (hvis du ikke allerede har det). SSH derefter ind på din fjernserver og kør:

$ sudo apt-get install supervisor 

Vi skal derefter fortælle Supervisor om vores Selleri-medarbejdere ved at tilføje konfigurationsfiler til mappen "/etc/supervisor/conf.d/" på fjernserveren. I vores tilfælde har vi brug for to sådanne konfigurationsfiler - en til Selleri-arbejderen og en til Selleri-planlæggeren.

Lokalt skal du oprette en mappe kaldet "supervisor" i projektets rod. Tilføj derefter følgende filer...

Selleriarbejder:picha_celery.conf

; ===================================; eksempel på vejleder for selleriarbejder; ===================================; navnet på dit supervisorprogram[program:pichacelery]; Indstil den fulde sti til selleriprogrammet, hvis du bruger virtualenvcommand=/home/mosh/.virtualenvs/picha/bin/selleriarbejder -A picha --loglevel=INFO; Mappen til din Django-projektmappe=/home/mosh/sites/picha; Hvis supervisord køres som root-bruger, skal du skifte brugere til denne UNIX-brugerkonto; før du gør noget processing.user=mosh; Supervisor vil starte så mange forekomster af dette program som navngivet af numprocsnumprocs=1; Sæt proces stdout output i denne filestdout_logfile=/var/log/celery/picha_worker.log; Sæt proces stderr output i denne filestderr_logfile=/var/log/celery/picha_worker.log; Hvis det er sandt, starter dette program automatisk, når supervisord startes autostart=true; Kan være falsk, uventet eller sand. Hvis det er falsk, vil processen aldrig; blive automatisk genstartet. Hvis det er uventet, genstartes processen, når programmet; udgange med en udgangskode, der ikke er en af ​​de udgangskoder, der er forbundet hermed; proces' konfiguration (se udgangskoder). Hvis det er sandt, vil processen være; ubetinget genstartet, når den afsluttes, uden hensyntagen til dens udgangskode.autorestart=true; Det samlede antal sekunder, som programmet skal forblive kørende efter; en opstart for at betragte starten som vellykket.startsecs=10; Skal vente på, at opgaver, der udføres i øjeblikket, afsluttes ved nedlukning.; Forøg dette, hvis du har meget lang kørende tasks.stopwaitsecs =600; Når man tyr til at sende SIGKILL til programmet for at afslutte det; send SIGKILL til hele sin procesgruppe i stedet,; at tage sig af sine børn også.killasgroup=true; hvis din mægler er overvåget, sæt dens prioritet højere; så det starter firstpriority=998

Selleriplanlægning:picha_celerybeat.conf

; =================================; selleri beat supervisor eksempel; =================================; navnet på dit supervisorprogram[program:pichacelerybeat]; Indstil den fulde sti til selleriprogrammet, hvis du bruger virtualenvcommand=/home/mosh/.virtualenvs/picha/bin/celerybeat -A picha --loglevel=INFO; Mappen til din Django-projektmappe=/home/mosh/sites/picha; Hvis supervisord køres som root-bruger, skal du skifte brugere til denne UNIX-brugerkonto; før du gør noget processing.user=mosh; Supervisor vil starte så mange forekomster af dette program som navngivet af numprocsnumprocs=1; Sæt proces stdout output i denne filestdout_logfile=/var/log/celery/picha_beat.log; Sæt proces stderr output i denne filestderr_logfile=/var/log/celery/picha_beat.log; Hvis det er sandt, starter dette program automatisk, når supervisord startes autostart=true; Kan være falsk, uventet eller sand. Hvis det er falsk, vil processen aldrig; blive automatisk genstartet. Hvis det er uventet, genstartes processen, når programmet; udgange med en udgangskode, der ikke er en af ​​de udgangskoder, der er forbundet hermed; proces' konfiguration (se udgangskoder). Hvis det er sandt, vil processen være; ubetinget genstartet, når den afsluttes, uden hensyntagen til dens udgangskode.autorestart=true; Det samlede antal sekunder, som programmet skal forblive kørende efter; en opstart for at betragte starten som vellykket.startsecs=10; hvis din mægler er overvåget, sæt dens prioritet højere; så det starter firstpriority=999

Sørg for at opdatere stierne i disse filer, så de matcher fjernserverens filsystem.

Grundlæggende fortæller disse supervisor-konfigurationsfiler supervisor, hvordan man kører og administrerer vores 'programmer' (som de kaldes af supervisor).

I eksemplerne ovenfor har vi oprettet to supervisord-programmer ved navn "pichacelery" og "pichacelerybeat".

Kopier nu bare disse filer til fjernserveren i mappen "/etc/supervisor/conf.d/".

Vi skal også oprette de logfiler, der er nævnt i ovenstående scripts på fjernserveren:

$ touch /var/log/celery/picha_worker.log$ touch /var/log/celery/picha_beat.log 

Til sidst skal du køre følgende kommandoer for at gøre Supervisor opmærksom på programmerne - f.eks. pichacelery og pichacelerybeat :

$ sudo supervisorctl genlæs$ sudo supervisorctl opdatering 

Kør følgende kommandoer for at stoppe, starte og/eller kontrollere status for pichacelery program:

$ sudo supervisorctl stop pichacelery$ sudo supervisorctl start pichacelery$ sudo supervisorctl status pichacelery 

Du kan læse mere om Supervisor fra den officielle dokumentation.



Sidste tip

  1. Undlad at videregive Django-modelobjekter til Selleri-opgaver. For at undgå tilfælde, hvor modelobjektet allerede er ændret, før det videregives til en Selleri-opgave, skal du videregive objektets primære nøgle til Selleri. Du skal så selvfølgelig bruge den primære nøgle til at hente objektet fra databasen, før du arbejder på det.
  2. Standardplanlægningen for Celery opretter nogle filer for at gemme dens tidsplan lokalt. Disse filer ville være "celerybeat-schedule.db" og "celerybeat.pid". Hvis du bruger et versionskontrolsystem som Git (hvilket du burde!), er det en god idé at ignorere disse filer og ikke tilføje dem til dit lager, da de er til at køre processer lokalt.


Næste trin

Nå, det er det for den grundlæggende introduktion til at integrere selleri i et Django-projekt.

Vil du have mere?

  1. Dyk ned i den officielle Selleri-brugervejledning for at lære mere.
  2. Opret en Fab-fil for at opsætte Supervisor og konfigurationsfilerne. Sørg for at tilføje kommandoerne til genlæse og opdatering Supervisor.
  3. Forkast projektet fra repoen, og åbn en pull-anmodning for at tilføje en ny selleri-opgave.

Gratis bonus: Klik her for at få adgang til en gratis Django Learning Resources Guide (PDF), der viser dig tips og tricks samt almindelige faldgruber, du skal undgå, når du bygger Python + Django-webapplikationer.

God kodning!



  1. Brug af MySQL-klientapplikationer

  2. Ydeevnejustering af hele forespørgselsplanen

  3. Forespørgselskombinationer med indlejret matrix af poster i JSON-datatype

  4. Tre nemme SQL Server Performance-gevinster