Webapplikasjoner starter vanligvis enkelt, men kan bli ganske komplekse, og de fleste av dem overgår raskt ansvaret for bare å svare på HTTP-forespørsler.
Når det skjer, må man skille mellom hva som må skje umiddelbart (vanligvis i HTTP-forespørselslivet) og hva som kan skje til slutt. Hvorfor det? Vel, fordi når søknaden din blir overbelastet med trafikk, gjør enkle ting som dette forskjellen.
Operasjoner i en webapplikasjon kan klassifiseres som kritisk eller forespørselstidsoperasjoner og bakgrunnsoppgaver, de som skjer utenfor forespørselstid. Disse kartene til de som er beskrevet ovenfor:
Forespørselstidsoperasjoner kan gjøres på en enkelt forespørsel / svarssyklus uten å bekymre seg for at operasjonen vil gå ut, eller at brukeren kan ha en dårlig opplevelse. Vanlige eksempler inkluderer CRUD (Opprett, Les, Oppdater, Slett) databaseoperasjoner og brukeradministrasjon (Logg inn / Logg ut rutiner).
Bakgrunnsoppgaver er forskjellige da de vanligvis er ganske tidkrevende og er utsatt for feil, hovedsakelig på grunn av eksterne avhengigheter. Noen vanlige scenarier blant komplekse webapplikasjoner inkluderer:
Bakgrunnsoppgaver er hovedfokus for denne opplæringen. Det vanligste programmeringsmønsteret som brukes for dette scenariet, er produsentens forbrukerarkitektur.
Enkelt sagt, kan denne arkitekturen beskrives slik:
Vanligvis henter forbrukerne oppgaver fra køen i en første-i-første-ut-modus (FIFO) eller i henhold til deres prioriteringer. Forbrukerne er også referert til som arbeidere, og det er begrepet vi skal bruke hele, da det er i tråd med terminologien som brukes av teknologiene som diskuteres.
Hva slags oppgaver kan behandles i bakgrunnen? Oppgaver som:
Selleri er de facto valget for å gjøre bakgrunnsoppgavebehandling i Python / Django-økosystemet. Den har en enkel og klar API, og den integrerer vakkert med Django. Den støtter ulike teknologier for oppgavekøen og ulike paradigmer for arbeiderne.
I denne opplæringen skal vi lage et Django-leketøy-webprogram (som omhandler virkelige scenarier) som bruker bakgrunnsoppgavebehandling.
Forutsatt at du allerede er kjent med Python pakkebehandling og virtuelle miljøer, la oss installere Django:
$ pip installere Django
Jeg har bestemt meg for å bygge enda en blogging-applikasjon. Fokuset på søknaden vil være på enkelhet. En bruker kan ganske enkelt opprette en konto og uten for mye oppstyr kan du lage et innlegg og publisere det på plattformen.
Sett opp quick_publisher
Django-prosjektet:
$ django-admin startproject quick_publisher
La oss få appen startet:
$ cd quick_publisher $ ./manage.py startapp main
Når jeg starter et nytt Django-prosjekt, liker jeg å lage en hoved-
Program som inneholder blant annet en egendefinert brukermodell. Oftere enn ikke, støter jeg på begrensninger av standard Django Bruker
modell. Å ha en skikk Bruker
modellen gir oss fordelen av fleksibilitet.
# main / models.py fra django.db importmodeller fra django.contrib.auth.models importerer AbstractBaseUser, PermissionsMixin, BaseUserManager-klassen UserAccountManager (BaseUserManager): use_in_migrations = True def _create_user (selv, e-post, passord, ** ekstrafelt): hvis ikke e-post: heve ValueError ('Email-adresse må oppgis') hvis ikke passord: oppring ValueError ('Passord må oppgis') email = self.normalize_email (email) user = self.model (email = email, ** extra_fields) user.set_password (passord) user.save (using = self._db) returner bruker def create_user (selv, email = Ingen, passord = Ingen, ** ekstra_felt): returner selv._create_user (email, passord, ** extra_fields) def create_superuser (self, email, passord, ** extra_fields): extra_fields ['is_staff'] = Sann ekstra_fields ['is_superuser'] = Sann tilbakemelding selv._create_user (email, passord, ** extra_fields) klasse Bruker (AbstractBaseUser, PermissionsMixin): REQUIRED_FIELDS = [] USERNAME_FIELD = 'email' objekter = UserAccountManager () email = models.EmailField ('e-post', unikt = True, blank = False, null = False) full_name = models.CharField ('fullt navn', blank = True, null = True, max_length = 400) is_staff = models.BooleanField , default = False) is_active = models.BooleanField ('aktiv', standard = True) def get_short_name (selv): return self.email def get_full_name (selv): return self.email def __unicode __ (selv): returner self.email
Sørg for å sjekke ut Django-dokumentasjonen hvis du ikke er kjent med hvordan egendefinerte brukermodeller fungerer.
Nå må vi fortelle Django å bruke denne brukermodellen i stedet for den vanlige. Legg denne linjen til quick_publisher / settings.py
fil:
AUTH_USER_MODEL = 'main.User'
Vi må også legge til hoved-
søknad til INSTALLED_APPS
liste i quick_publisher / settings.py
fil. Vi kan nå opprette migreringer, bruke dem og opprette en superbruker for å kunne logge inn på Django admin panel:
$ ./manage.py makemigrations hoved $ ./manage.py migrere $ ./manage.py createsuperuser
La oss nå lage en egen Django-applikasjon som er ansvarlig for innlegg:
$ ./manage.py startapp publisering
La oss definere en enkel Post-modell i utgiver / models.py
:
fra django.db importmodeller fra django.utils importere tidszone fra django.contrib.auth importere get_user_model klasse Post (models.Model): author = models.ForeignKey (get_user_model ()) created = models.DateTimeField ('Opprettet dato', standard = timezone.now) title = models.CharField ('Tittel', max_length = 200) content = models.TextField ('Innhold') slug = models.SlugField ('Slug') def __str __ (selv): return "av% s '% (self.title, self.author)
Hooking Post
modell med Django admin er ferdig i utgiver / admin.py
filen som denne:
fra django.contrib import admin fra .models import Post @ admin.register (Post) klasse PostAdmin (admin.ModelAdmin): pass
Til slutt, la oss kroke forlegger
søknad med vårt prosjekt ved å legge det til INSTALLED_APPS
liste.
Vi kan nå kjøre serveren og gå over til http: // localhost: 8000 / admin /
og opprett våre første innlegg slik at vi har noe å leke med:
$ ./manage.py runserver
Jeg stoler på at du har gjort leksene dine, og du har opprettet innleggene.
La oss gå videre. Det neste åpenbare skrittet er å skape en måte å vise publiserte innlegg på.
# publisher / views.py fra django.http import Http404 fra django.shortcuts import gjengi fra .models import Post def view_post (forespørsel, slug): prøv: post = Post.objects.get (slug = slug) unntatt Post.DoesNotExist: heve Http404 ("Poll eksisterer ikke") returnere gjengivelse (forespørsel, 'post.html', context = ('post': post)
La oss knytte vår nye visning med en URL i: quick_publisher / urls.py
# quick_publisher / urls.py fra django.conf.urls import url fra django.contrib import admin fra publisher.views import view_post urlpatterns = [url (r '^ admin /', admin.site.urls), url (r '^ (? P[a-zA-Z0-9 \ -] +) ', view_post, name = "view_post")]
Til slutt, la oss lage malen som gjør innlegget i: utgiver / templates / post.html
post.title
post.content
Publisert av post.author.full_name på post.created
Vi kan nå gå til http: // localhost: 8000 / slug-of-the-post-you-created / i nettleseren. Det er ikke akkurat et mirakel av webdesign, men å lage flotte innlegg er utenfor omfanget av denne opplæringen.
Her er det klassiske scenariet:
La oss legge til en is_verified
flagg og verification_uuid
på Bruker
modell:
# main / models.py import uuid class Bruker (AbstractBaseUser, PermissionsMixin): REQUIRED_FIELDS = [] USERNAME_FIELD = 'email' objekter = UserAccountManager () email = models.EmailField ('email', unikt = True, blank = False, null = False) full_name = models.CharField ('fullt navn', blank = True, null = True, max_length = 400) is_staff = models.BooleanField ('stabsstatus', default = False) is_active = models.BooleanField ('aktiv' default = True) is_verified = models.BooleanField ('verifisert', standard = False) # Legg til 'is_verified' flagg verification_uuid = models.UUIDField ('Unik verifisering UUID', default = uuid.uuid4) def get_short_name self.email def get_full_name (selv): return self.email def __unicode __ (selv): returner self.email
La oss bruke denne anledningen til å legge til brukermodellen til administratoren:
fra django.contrib import admin fra .models import Bruker @ admin.register (User) class UserAdmin (admin.ModelAdmin): pass
La oss gjøre endringene gjenspeilet i databasen:
$ ./manage.py makemigrations $ ./manage.py migrere
Vi må nå skrive et stykke kode som sender en e-post når en brukereksempel er opprettet. Dette er hva Django-signaler er for, og dette er en perfekt anledning til å berøre dette emnet.
Signaler blir sparket før / etter at visse hendelser oppstår i søknaden. Vi kan definere tilbakeringingsfunksjoner som utløses automatisk når signalene blir sparket. For å lage en tilbakeringingsutløseren må vi først koble den til et signal.
Vi skal lage et tilbakering som vil bli utløst etter at en brukermodell er opprettet. Vi legger til denne koden etter Bruker
modelldefinisjon i: Hoved / models.py
fra django.db.models importere signaler fra django.core.mail import send_mail def user_post_save (sender, forekomst, signal, * args, ** kwargs): hvis ikke instance.is_verified: # Send verifikasjons-epost send_mail ('Bekreft QuickPublisher-kontoen din ',' Følg denne lenken for å bekrefte kontoen din: 'http: // localhost: 8000% s'% reverse ('verifiser', kwargs = 'uuid': str (instance.verification_uuid)), 'fra @ quickpublisher. dev ', [instance.email], fail_silently = False,) signals.post_save.connect (user_post_save, sender = bruker)
Det vi har gjort her er, vi har definert a user_post_save
funksjon og tilkoblet den til post_save
signal (en som utløses etter at en modell er lagret) sendt av Bruker
modell.
Django sender ikke bare e-post ut på egen hånd; det må knyttes til en e-posttjeneste. For enkelhets skyld kan du legge til Gmail-legitimasjonene dine i quick_publisher / settings.py
, eller du kan legge til din favoritt e-postleverandør.
Her ser du hvordan Gmail-konfigurasjonen ser ut:
EMAIL_USE_TLS = EMAIL_HOST = 'smtp.gmail.com' EMAIL_HOST_USER = '@ gmail.com 'EMAIL_HOST_PASSWORD =' 'EMAIL_PORT = 587
For å teste ut ting, gå inn på administrasjonspanelet og opprett en ny bruker med en gyldig e-postadresse du raskt kan sjekke. Hvis alt gikk bra, vil du motta en e-post med en bekreftelseskobling. Verifikasjonsrutinen er ikke klar ennå.
Slik bekrefter du kontoen:
# main / views.py fra django.http import Http404 fra django.shortcuts import render, omdirigere fra .models import Bruker def home (request): return render (request, 'home.html') def bekrefte (forespørsel, uuid): Prøv: user = User.objects.get (verification_uuid = uuid, is_verified = False) unntatt User.DoesNotExist: heve Http404 ("Brukeren eksisterer ikke eller er allerede bekreftet") user.is_verified = Ekte user.save () returnere omdirigering () 'hjem')
Koble utsikten opp i: quick_publisher / urls.py
# quick_publisher / urls.py fra django.conf.urls import url fra django.contrib import admin fra publisher.views import view_post fra main.views importere hjem, bekreft urlpatterns = [url (r '^ $', hjem, navn = " hjemme "), url (r '^ admin /', admin.site.urls), url (r '^ verifiser / (P[a-z0-9 \ -] +) / ', verifiser, navn = "verifiser"), url (r' ^ (P [a-zA-Z0-9 \ -] +) ', view_post, name = "view_post")]
Husk også å opprette en home.html
filen under Hoved / templates / home.html
. Det vil bli gjengitt av hjem
utsikt.
Prøv å kjøre hele scenariet igjen. Hvis alt er bra, vil du motta en e-post med en gyldig bekreftelsesadresse. Hvis du følger nettadressen og deretter sjekker inn administrasjonen, kan du se hvordan kontoen er verifisert.
Her er problemet med det vi har gjort hittil. Du har kanskje lagt merke til at det å opprette en bruker er litt treg. Det er fordi Django sender verifikasjonsmeldingen innen forespørselstidspunktet.
Slik fungerer det: Vi sender brukerdata til Django-applikasjonen. Programmet oppretter en Bruker
modell og oppretter deretter en forbindelse til Gmail (eller en annen tjeneste du har valgt). Django venter på svaret, og bare da returnerer det et svar til nettleseren vår.
Her er hvor Selleri kommer inn. Først må du kontrollere at det er installert:
$ pip installere Selleri
Vi trenger nå å lage et Selleri-program i vår Django-applikasjon:
# quick_publisher / celery.py import oss fra selleri import Selder os.environ.setdefault ('DJANGO_SETTINGS_MODULE', 'quick_publisher.settings') app = Selleri ('quick_publisher') app.config_from_object ('django.conf: settings') # Last oppgavemoduler fra alle registrerte Django app konfigs. app.autodiscover_tasks ()
Selleri er en oppgavekø. Den mottar oppgaver fra vår Django-applikasjon, og den vil kjøre dem i bakgrunnen. Selleri må paires med andre tjenester som fungerer som meglere.
Meglere mellom sending av meldinger mellom webapplikasjon og Selleri. I denne opplæringen bruker vi Redis. Redis er enkel å installere, og vi kan lett komme i gang med det uten for mye oppstyr.
Du kan installere Redis ved å følge instruksjonene på Redis hurtigstartside. Du må installere Redis Python biblioteket, pip installere redis
, og bunken som er nødvendig for bruk av Redis og Selleri: pip installere selleri [redis]
.
Start Redis-serveren i en separat konsoll som denne: $ redis-server
La oss legge til Celery / Redis-relaterte configs i quick_publisher / settings.py
:
# REDIS relaterte innstillinger REDIS_HOST = 'localhost' REDIS_PORT = '6379' BROKER_URL = 'redis: //' + REDIS_HOST + ':' + REDIS_PORT + '/ 0' BROKER_TRANSPORT_OPTIONS = 'visibility_timeout': 3600 CELERY_RESULT_BACKEND = 'redis: / / '+ REDIS_HOST +': '+ REDIS_PORT +' / 0 '
Før alt kan kjøres i Selleri, må det deklareres som en oppgave.
Slik gjør du dette:
# main / tasks.py import logging fra django.urls importere omvendt fra django.core.mail import send_mail fra django.contrib.auth import get_user_model fra quick_publisher.celery import app @ app.task def send_verification_email (user_id): UserModel = get_user_model ( ) Prøv: user = UserModel.objects.get (pk = user_id) send_mail ('Bekreft QuickPublisher-kontoen din', 'Følg denne lenken for å bekrefte kontoen din: http: // localhost: 8000% s'% reverse , Kwargs = 'uuid': str (user.verification_uuid)), '[email protected]', [user.email], fail_silently = False,) unntatt UserModel.DoesNotExist: logging.warning ("Forsøkte å sende verifisering e-post til ikke-eksisterende bruker '% s' "% user_id)
Det vi har gjort her er dette: Vi flyttet sendingsbekreftelses-e-postfunksjonen i en annen fil som heter tasks.py
.
Noen få notater:
INSTALLED_APPS
og registrerer oppgavene i tasks.py
filer.send_verification_email
fungere med @ app.task
. Dette forteller Selleri Dette er en oppgave som skal kjøres i oppgavekøen.bruker-ID
snarere enn a Bruker
gjenstand. Dette skyldes at vi kan ha problemer med å seriere komplekse objekter når du sender oppgavene til Selleri. Det er best å holde dem enkle.Kommer tilbake til Hoved / models.py
, signalkoden blir til:
fra django.db.models importere signaler fra main.tasks import send_verification_email def user_post_save (sender, forekomst, signal, * args, ** kwargs): hvis ikke instance.is_verified: # Send verifiserings-epost send_verification_email.delay (instance.pk) signaler .post_save.connect (user_post_save, sender = bruker)
Legg merke til hvordan vi kaller .utsette
metode på oppgaveobjektet. Dette betyr at vi sender oppgaven av til Selleri, og vi venter ikke på resultatet. Hvis vi brukte send_verification_email (instance.pk)
I stedet vil vi fortsatt sende det til Selleri, men vil vente på oppgaven å fullføre, noe som ikke er det vi ønsker.
Før du begynner å lage en ny bruker, er det en fangst. Selleri er en tjeneste, og vi må starte det. Åpne en ny konsoll, pass på at du aktiverer riktig virtualenv, og naviger til prosjektmappen.
$ selleri-en quick_publisher --loglevel = debug --concurrency = 4
Dette starter fire selleri prosess arbeidere. Ja, nå kan du endelig gå og skape en annen bruker. Legg merke til hvordan det ikke er forsinkelser, og sørg for å se loggene i selgerkonsollen og se om oppgavene er riktig utført. Dette skulle se slik ut:
[2017-04-28 15: 00: 09,190: DEBUG / MainProcess] Oppgave akseptert: main.tasks.send_verification_email [f1f41e1f-ca39-43d2-a37d-9de085dc99de] pid: 62065 [2017-04-28 15: 00: 11,740: INFO / PoolWorker-2] Oppgave main.tasks.send_verification_email [f1f41e1f-ca39-43d2-a37d-9de085dc99de] lyktes i 2.5500912349671125s: Ingen
Her er et annet vanlig scenario. De fleste modne webapplikasjoner sender brukerne livscyklusmeldinger for å holde dem engasjert. Noen vanlige eksempler på livssyklus e-post:
Her er hva vi skal gjøre i vår app. Vi skal telle hvor mange ganger hvert innlegg har blitt sett og sende en daglig rapport til forfatteren. En gang hver eneste dag skal vi gå gjennom alle brukerne, hente innleggene sine og sende en epost med et bord som inneholder innleggene og vise teller.
La oss endre Post
modell slik at vi kan imøtekomme synsfelt scenariet.
klasse Post (models.Model): author = models.ForeignKey (User) created = models.DateTimeField ('Opprettet dato', default = timezone.now) title = models.CharField ('Tittel', max_length = 200) innhold = modeller .TextField ('Innhold') slug = models.SlugField ('Slug') view_count = models.IntegerField ("Vis antall", standard = 0) def __str __ (selv): returner% s '% self.title, self.author)
Som alltid, når vi endrer en modell, må vi migrere databasen:
$ ./manage.py makemigrations $ ./manage.py migrere
La oss også endre view_post
Django-visning for å telle visninger:
def view_post (request, slug): prøv: post = Post.objects.get (slug = slug) unntatt Post.DoesNotExist: raise Http404 ("Poll eksisterer ikke") post.view_count + = 1 post.save () return render (forespørsel, 'post.html', context = ('post': post)
Det ville være nyttig å vise Sett post.view_count gangerseertall
i malen. Legg til dette
et sted inne i utgiver / templates / post.html
fil. Gjør et par visninger på et innlegg nå og se hvordan disken øker.
La oss lage en Selleri-oppgave. Siden det handler om innlegg, skal jeg legge den inn utgiver / tasks.py
:
fra django.template import Mal, Kontekst fra django.core.mail import send_mail fra django.contrib.auth import get_user_model fra quick_publisher.celery import app fra publisher.models import Post REPORT_TEMPLATE = "" "Slik har du gjort nå: % for innlegg i innlegg% "post.title": sett post.view_count ganger | % endfor% "" "@ app.task def send_view_count_report (): for bruker i get_user_model (). objects.all (): posts = Post.objects.filter (forfatter = bruker) hvis ikke innlegg: fortsett template = Template (REPORT_TEMPLATE) send_mail ('Din QuickPublisher Aktivitet', template.render (context = Context ('posts' innlegg), '[email protected]', [user.email], fail_silently = False,)
Hver gang du gjør endringer i Selleri-oppgaver, husk å gjenoppta Selleri-prosessen. Selleri må oppdage og omlaste oppgaver. Før du oppretter en periodisk oppgave, bør vi teste dette ut i Django-skallet for å sikre at alt fungerer som ønsket:
$ ./manage.py shell i [1]: fra publisher.tasks importerer send_view_count_report I [2]: send_view_count_report.delay ()
Forhåpentligvis mottok du en fin liten rapport i e-posten din.
La oss nå lage en periodisk oppgave. Åpne opp quick_publisher / celery.py
og registrer de periodiske oppgavene:
# quick_publisher / celery.py import oss fra selleri import Selleri fra selleri.schemaer importere crontab os.environ.setdefault ('DJANGO_SETTINGS_MODULE', 'quick_publisher.settings') app = Selleri ('quick_publisher') app.config_from_object ('django.conf : innstillinger ') # Last opp oppgavemoduler fra alle registrerte Django app konfigs. app.autodiscover_tasks () app.conf.beat_schedule = 'send-report-every-single-minute': 'oppgave': 'publisher.tasks.send_view_count_report', 'schedule': crontab (), # endre til 'crontab (minutt = 0, time = 0) 'hvis du vil at den skal løpe daglig om midnatt,
Så langt har vi opprettet en tidsplan som skulle løpe oppgaven publisher.tasks.send_view_count_report
hvert minutt som angitt av crontab ()
notasjon. Du kan også angi forskjellige Celery Crontab-tidsplaner.
Åpne en annen konsoll, aktiver det riktige miljøet, og start Celery Beat-tjenesten.
$ selleri-en quick_publisher slå
Beat-tjenesten er å skape oppgaver i Selleri i henhold til tidsplanen. Ta hensyn til at timeplanen gjør send_view_count_report
Oppgave løp hvert minutt i henhold til oppsettet. Det er bra for testing, men ikke anbefalt for en ekteverden webapplikasjon.
Oppgaver blir ofte brukt til å utføre upålitelige operasjoner, operasjoner som er avhengige av eksterne ressurser eller som lett kan mislykkes på grunn av ulike årsaker. Her er en veiledning for å gjøre dem mer pålitelige:
Jeg håper dette har vært en interessant opplæring for deg og en god introduksjon til bruk av Selleri med Django.
Her er noen konklusjoner vi kan tegne:
selleri beat
service.