Aplicațiile Web încep de obicei simplu, dar pot deveni destul de complexe, iar majoritatea dintre ele depășesc rapid responsabilitatea de a răspunde numai la cererile HTTP.
Când se întâmplă acest lucru, trebuie făcut o distincție între ceea ce trebuie să se întâmple instantaneu (de obicei în ciclul de viață al cererii HTTP) și ce se poate întâmpla în cele din urmă. De ce este asta? Ei bine, pentru că atunci când aplicația dvs. devine supraîncărcată cu trafic, lucruri simple ca asta fac diferența.
Operațiile dintr-o aplicație web pot fi clasificate ca operațiuni critice sau care necesită timp și sarcini de fundal, cele care se petrec în afara timpului de solicitare. Aceste hărți sunt cele descrise mai sus:
Operațiunile de timp de solicitare pot fi efectuate pe un singur ciclu de solicitare / răspuns, fără a vă face griji că operațiunea se va opri sau că utilizatorul ar putea avea o experiență defectuoasă. Exemplele obișnuite includ operațiile de baze de date CRUD (Creare, citire, actualizare, ștergere) și gestionarea utilizatorilor (rutine de conectare / deconectare).
Sarcinile de fundal sunt diferite, deoarece acestea sunt, de obicei, destul de consumatoare de timp și sunt predispuse la eșec, în mare parte datorită dependențelor externe. Unele scenarii comune printre aplicațiile web complexe includ:
Obiectivele principale sunt principalul obiectiv al acestui tutorial. Cel mai obișnuit model de programare folosit pentru acest scenariu este Arhitectura consumatorilor de produse.
În termeni simpli, această arhitectură poate fi descrisă astfel:
În mod obișnuit, consumatorii preiau sarcini din coadă într-o primă clasă (FIFO) sau în funcție de prioritățile lor. Consumatorii sunt, de asemenea, numiți muncitori, și acesta este termenul pe care îl vom folosi în întregime, deoarece este în concordanță cu terminologia utilizată de tehnologiile discutate.
Ce fel de sarcini pot fi procesate în fundal? Sarcini care:
Țelina este alegerea de facto pentru a face procesarea sarcinilor de fond în ecosistemul Python / Django. Are un API simplu și clar și se integrează frumos cu Django. Acesta susține diverse tehnologii pentru coada de sarcini și diverse paradigme pentru lucrători.
În acest tutorial, vom crea o aplicație web pentru jucării Django (care se ocupă de scenarii din lumea reală) care utilizează procesarea sarcinilor de fundal.
Presupunând că deja cunoașteți managementul pachetelor Python și mediile virtuale, să instalați Django:
$ pip instala Django
Am decis să construiesc o altă aplicație de blogging. Aplicația se va concentra pe simplitate. Un utilizator poate crea un cont simplu și fără prea multă agitație poate crea un post și îl poate publica platformei.
Configurați quick_publisher
Proiectul Django:
$ django-admin startproject rapid_publisher
Să începem aplicația:
$ cd rapid_publisher $ ./manage.py startapp main
Când încep un nou proiect Django, îmi place să creez un principal
aplicație care conține, printre altele, un model de utilizator personalizat. De cele mai multe ori, mă întâlnesc cu limitări ale implicit Django Utilizator
model. Având un obicei Utilizator
modelul ne oferă beneficiul flexibilității.
# main / models.py din modelele de import django.db de la importul django.contrib.auth.models AbstractBaseUser, PermissionsMixin, BaseUserManager clasa UserAccountManager (BaseUserManager): use_in_migrations = True def _create_user (auto, email, parola, ** extra_fields): if nu e-mail: ridicați ValueError ("Adresa de e-mail trebuie furnizată") dacă nu parola: ridicați ValueError ("Parola trebuie furnizată") email = self.normalize_email (email) user.set_password (parola) user.save (folosind = auto._db) returnează user def create_user (auto, email = Niciuna, parola = Niciuna, ** extra_fields): return self._create_user (e-mail, parola, create_superuser (e-mail, parola, ** extra_fields): extra_fields ['is_staff'] = Adevărat extra_fields ['is_superuser'] = Adevărat întoarcere self._create_user (e-mail, parola, ** extra_fields) User (AbstractBaseUser, PermissionsMixin): REQUIRED_FIELDS = [] USERNAME_FIELD = 'email' obiecte = UserAccountManager () email = models.EmailField (e-mail), unic = Adevărat, gol = False, null = False) full_name = models.CharField (' , default = False) este_activ = models.BooleanField ('activ', default = True) def get_short_name (auto): return self.email def get_full_name (self)
Asigurați-vă că verificați documentația Django dacă nu sunteți familiarizat cu modul în care funcționează modelele personalizate ale utilizatorilor.
Acum trebuie să-i spunem lui Django să utilizeze acest model de utilizator în locul celui implicit. Adăugați această linie la quick_publisher / settings.py
fişier:
AUTH_USER_MODEL = 'main.User'
Trebuie, de asemenea, să adăugăm principal
aplicație la INSTALLED_APPS
listă în quick_publisher / settings.py
fişier. Acum putem crea migrațiile, le putem aplica și putem crea un superuser pentru a putea intra în panoul de administrare Django:
$ ./manage.py makemigrations principal $ ./manage.py migrează $ ./manage.py createsuperuser
Să creați acum o aplicație separată Django responsabilă de postări:
$ ./manage.py startapp publicați
Să definim un model Post simplu în editor / models.py
:
din modelele de import django.db din fusul orar de import django.utils de la django.contrib.auth importul get_user_model class Post (models.Model): author = models.ForeignKey (get_user_model ()) create = models.DateTimeField (' = timezone.now) title = models.CharField ('Titlu', max_length = 200) content = models.TextField ('Conținut') slug = models.SlugField ('Slug') def __str __ (self) "de% s '% (auto.titlu, auto.author)
Încărcarea Post
modelul cu adminul Django se face în editor / admin.py
fișier ca acesta:
de la importul django.contrib admin de la .models import Post @ admin.register (Post) class PostAdmin (admin.ModelAdmin): treci
În cele din urmă, hai să-l prindem editor
aplicație cu proiectul nostru prin adăugarea acestuia la INSTALLED_APPS
listă.
Putem rula acum serverul și să ne îndreptăm spre http: // localhost: 8000 / admin /
și să creați primele noastre posturi, astfel încât să avem de jucat:
$ ./manage.py runserver
Am încredere că ți-ai făcut temele și că ai creat postările.
Sa trecem peste. Următorul pas evident este de a crea o modalitate de a vizualiza postările publicate.
# editor / views.py din importul django.http Http404 de la django.shortcuts importa renderul din importul .models Post post view_post (request, slug): try: post = Post.objects.get (slug = slug) cu excepția Post.DoesNotExist: ridicați Http404 ("Sondaj nu există") retur render (cerere, 'post.html', context = 'post': post)
Să asociem noua noastră viziune cu o adresă URL în: quick_publisher / urls.py
# rapid_publisher / urls.py de la django.conf.urls import url din django.contrib import admin din editor.views import view_post urlpatterns = [url (r '^ admin /', admin.site.urls), url (r '^ (? P[a-zA-Z0-9 \ "] +) ', view_post, name =" view_post ")]
În cele din urmă, să creăm șablonul care redă postarea în: editor / template-uri / post.html
post.title
post.content
Publicat de post.author.full_name pe post.created
Acum ne putem îndrepta către http: // localhost: 8000 / the-slug-of-the-post-ați creat / în browser. Nu este chiar un miracol al designului web, dar realizarea unor posturi de calitate este dincolo de sfera acestui tutorial.
Iată scenariul clasic:
Să adăugăm un is_verified
steag și verification_uuid
pe Utilizator
model:
# principale / models.py import uuid class Utilizator (AbstractBaseUser, PermissionsMixin): REQUIRED_FIELDS = [] USERNAME_FIELD = 'email' obiecte = UserAccountManager () email = models.EmailField False) full_name = models.CharField ('nume complet', blanc = True, null = True, max_length = 400) is_staff = models.BooleanField (' implicit = Adevărat) is_verified = models.BooleanField ('verificat', implicit = False) # Adăugați pavilonul 'is_verified' verification_uuid = models.UUIDField (default UUID) auto.email def get_full_name (auto): return self.email def __unicode __ (self): return self.email
Să utilizăm această ocazie pentru a adăuga modelul utilizatorului la admin:
din administrarea importului django.contrib admin din .models import User @ admin.register (User) clasa UserAdmin (admin.ModelAdmin): pass
Să transformăm modificările în baza de date:
$ ./manage.py makemigrations $ ./manage.py migrați
Acum trebuie să scriem o bucată de cod care trimite un e-mail când este creată o instanță de utilizator. Acestea sunt semnalele Django și este o ocazie perfectă pentru a atinge acest subiect.
Semnalele sunt declanșate înainte / după apariția anumitor evenimente în aplicație. Putem defini funcții de apel invers care sunt declanșate automat când semnalele sunt declanșate. Pentru a face un trigger callback, trebuie mai întâi să îl conectăm la un semnal.
Vom crea un apel invers care va fi declanșat după ce a fost creat un model de utilizator. Vom adăuga acest cod după Utilizator
definiția modelului în: principal / models.py
de la django.db.models semnale de import de la django.core.mail import send_mail def user_post_save (expeditor, instanță, semnal, * args, ** kwargs): dacă nu instance.is_verified: # Trimite e-mail de verificare send_mail ('Verificați contul QuickPublisher ',' Urmați acest link pentru a vă verifica contul: 'http: // localhost: 8000% s'% reverse ('verificare', kwargs = 'uuid': str (instance.verification_uuid)). dev ', [instance.email], fail_silently = False,) signals.post_save.connect (user_post_save, sender = Utilizator)
Ce am făcut aici este că am definit a user_post_save
funcția și conectat la post_save
semnal (unul care este declanșat după ce un model a fost salvat) trimis de către Utilizator
model.
Django nu trimite doar e-mailuri pe cont propriu; trebuie să fie legat de un serviciu de e-mail. Din motive de simplitate, puteți adăuga acreditările dvs. Gmail quick_publisher / settings.py
, sau puteți adăuga furnizorul dvs. de e-mail preferat.
Iată cum arată configurația Gmail:
EMAIL_USE_TLS = Adevărat EMAIL_HOST = 'smtp.gmail.com' EMAIL_HOST_USER = '@ gmail.com 'EMAIL_HOST_PASSWORD =' 'EMAIL_PORT = 587
Pentru a testa lucrurile, accesați panoul de administrare și creați un utilizator nou cu o adresă de e-mail validă pe care o puteți verifica rapid. Dacă totul a mers bine, veți primi un e-mail cu un link de verificare. Rutina de verificare nu este încă pregătită.
Iată cum să verificați contul:
# main / views.py din importul django.http Http404 de la django.shortcuts importa render, redirecționează din importul .models User def home (cerere): retur render (request, 'home.html') def verifică (request, uuid): încercați: user = User.objects.get (verification_uuid = uuid, is_verified = False) cu excepția User.DoesNotExist: ridicați Http404 ("Utilizatorul nu există sau este deja verificat") user.is_verified = 'Acasă')
Cuplați vederile în: quick_publisher / urls.py
# rapid_publisher / urls.py de la django.conf.urls import url din django.contrib import admin de la publisher.views import view_post de la main.views home import, verifica urlpatterns = [url (r '^ $', home, name = " acasă "), url (r '^ admin /', admin.site.urls), url (r '^ verifică /[a-z0-9 \ -] +) / ', verificați, name =' verify '), url (r' ^ [a-zA-Z0-9 \ "] +) ', view_post, name =" view_post ")]
De asemenea, nu uitați să creați o home.html
fișier în conformitate cu principal / template-uri / home.html
. Acesta va fi oferit de către Acasă
vedere.
Încercați să rulați întregul scenariu din nou. Dacă totul este bine, veți primi un e-mail cu o adresă URL de verificare validă. Dacă veți urma adresa URL și apoi veți verifica administratorul, puteți vedea cum a fost confirmat contul.
Iată problema cu ceea ce am făcut până acum. S-ar putea să fi observat că crearea unui utilizator este cam încet. Asta pentru că Django trimite e-mailul de verificare în timpul solicitării.
Acesta este modul în care funcționează: trimitem datele utilizatorului în aplicația Django. Aplicația creează o Utilizator
și apoi creează o conexiune la Gmail (sau la alt serviciu pe care l-ați selectat). Django așteaptă răspunsul și numai atunci returnează un răspuns browserului nostru.
Aici este locul în care Țelina intră. Mai întâi, asigurați-vă că este instalat:
$ pip instalați Țelina
Acum trebuie să creăm o aplicație pentru țelină în aplicația noastră Django:
# quick_publisher / celery.py import os din importul de țelină import Celery os.environ.setdefault ('DJANGO_SETTINGS_MODULE', 'quick_publisher.settings') app = Țelar ('quick_publisher') app.config_from_object ('django.conf: module de lucru de la toate configurările de aplicații Django înregistrate. app.autodiscover_tasks ()
Țelina este o coadă de sarcini. El primește sarcini de la aplicația noastră Django și le va rula în fundal. Țelina trebuie să fie asociată cu alte servicii care acționează ca brokeri.
Brokerii intermediază trimiterea de mesaje între aplicația web și Celery. În acest tutorial, vom folosi Redis. Redis este ușor de instalat și putem începe cu ușurință fără prea multă agitație.
Puteți instala Redis urmând instrucțiunile de pe pagina Redis Quick Start. Va trebui să instalați biblioteca Redis Python, pip install redis
, și pachetul necesar pentru utilizarea Redis și Țelină: pip instalare telina [redis]
.
Porniți serverul Redis într-o consolă separată, după cum urmează: $ redis-server
Să adăugăm configurările legate de Celery / Redis quick_publisher / settings.py
:
Setările legate de REDIS 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 '
Înainte ca orice să poată fi rulat în Țelină, trebuie declarat ca o sarcină.
Iată cum se face acest lucru:
# main / tasks.py de logare de import de la django.urls import inversă de la django.core.mail import send_mail de la django.contrib.auth import get_user_model de la quick_publisher.celery import app @ app.task def send_verification_email (user_id): UserModel = get_user_model ( ) încercați: user = UserModel.objects.get (pk = user_id) send_mail ('Verificați contul dvs. QuickPublisher', 'Urmați acest link pentru a vă verifica contul: http: // localhost: 8000% , kwargs = 'uuid': str (user.verification_uuid)), '[email protected]', [user.email], fail_silently = False, exceptând UserModel.DoesNotExist: logging.warning mail la utilizatorul non-existent '% s' "% user_id)
Ceea ce am făcut aici este acesta: am mutat funcția de trimitere a e-mailului de verificare într-un alt fișier numit tasks.py
.
Câteva note:
INSTALLED_APPS
și înregistrează sarcinile în tasks.py
fișiere.Trimite emailul de verificare
funcția cu @ app.task
. Acest lucru spune lui Celery că aceasta este o sarcină care va fi rulată în coada de sarcini.numele de utilizator
mai degrabă decât a Utilizator
obiect. Acest lucru se datorează faptului că s-ar putea să avem probleme în serializarea obiectelor complexe atunci când trimitem sarcinile către Țelina. Cel mai bine este să le păstrați simplu.Revenind la principal / models.py
, codul semnalului se transformă în:
din django.db.models semnale de import din main.tasks import send_verification_email def user_post_save (expeditor, instanță, semnal, * args, ** kwargs): dacă nu instance.is_verified: # Trimite e-mail de verificare send_verification_email.delay (instance.pk) semnale .post_save.connect (user_post_save, sender = Utilizator)
Observați cum îl numim .întârziere
pe obiectul de activitate. Asta inseamna ca trimitem sarcina catre Celery si nu asteptam rezultatul. Dacă am fi folosit send_verification_email (instance.pk)
în schimb, l-am fi trimis în continuare la Țelie, dar am fi așteptat ca sarcina să termine, ceea ce nu este ceea ce vrem.
Înainte de a începe să creați un utilizator nou, există o captură. Țelina este un serviciu și trebuie să-l pornim. Deschideți o nouă consolă, asigurați-vă că activați virtualenv-ul corespunzător și navigați la dosarul proiectului.
$ lucrătoare de telina -A quick_publisher --loglevel = debug --concurrency = 4
Aceasta începe cu patru lucrători din procesele de țelină. Da, acum puteți merge în sfârșit și puteți crea un alt utilizator. Observați cum nu există întârziere și asigurați-vă că urmăriți jurnalele din consola Celery și vedeți dacă sarcinile sunt executate corect. Ar trebui să arate ceva de genul:
[2017-04-28 15: 00: 09,190: DEBUG / MainProcess] Sarcina acceptată: main.tasks.send_verification_email [f1f41e1f-ca39-43d2-a37d-9de085dc99de] pid: 62065 [2017-04-28 15: 00: 11.740: INFO / PoolWorker-2] Task main.tasks.send_verification_email [f1f41e1f-ca39-43d2-a37d-9de085dc99de] a reușit în 2.5500912349671125s: Niciuna
Iată un alt scenariu comun. Cele mai multe aplicații web mature trimit utilizatorii lor e-mailuri de ciclu de viață pentru a le menține angajați. Unele exemple comune de e-mailuri pe durata ciclului de viață:
Iată ce vom face în aplicația noastră. Vom număra de câte ori a fost vizionată fiecare postare și vom trimite un raport zilnic autorului. Odată în fiecare zi, vom trece prin toți utilizatorii, vom prelua postările și vom trimite un e-mail cu un tabel care conține postările și numărul de vizionări.
Să schimbăm Post
astfel încât să putem acomoda scenariul privind numărul de vizualizări.
Clasa Post (modele.Model): autor = models.ForeignKey (User) creat = models.DateTimeField ('Creat data', default = timezone.now) title = models.CharField ('Titlu', max_length = .TextField ("Conținut") slug = models.SlugField ("Slug") view_count = models.IntegerField ("Count Count", default = 0) def __str __ (self) auto.title, auto.author)
Ca întotdeauna, atunci când schimbăm un model, trebuie să migrăm baza de date:
$ ./manage.py makemigrations $ ./manage.py migrați
Să modificăm de asemenea view_post
Vizualizarea Django pentru a număra vizionările:
def_post (request, slug): încercați: post = Post.objects.get (slug = slug) cu excepția Post.DoesNotExist: ridicați Http404 ("Sondaj nu există") post.view_count + = 1 post.save (solicitare, 'post.html', context = 'post': post)
Ar fi util să afișați A văzut post.view_count oriVIEW_COUNT
în șablon. Adaugă asta
undeva înăuntru editor / template-uri / post.html
fişier. Faceți câteva vizionări pe un post acum și vedeți cum crește numărul contorului.
Să creăm o sarcină de țelină. Deoarece este vorba despre posturi, o să o introduc editor / tasks.py
:
de la importul django.template Template, Context din django.core.mail importul send_mail din django.contrib.auth importul get_user_model de la quick_publisher.celery importul de la import publisher.models Post REPORT_TEMPLATE = "" "Iată cum ați făcut până acum: % pentru post în postări% "post.title": vizualizat post.view_count ori | % endfor% "" "@ app.task def send_view_count_report (): pentru utilizator în get_user_model (). objects.all (): posts = Post.objects.filter (author = user) dacă nu postări: continue template = Template (REPORT_TEMPLATE) send_mail (' mesaje)), "de [email protected]", [user.email], fail_silently = False,)
De fiecare dată când efectuați modificări asupra sarcinilor de țelină, nu uitați să reporniți procesul de Țelină. Țelina trebuie să descopere și să reîncarce sarcinile. Înainte de a crea o sarcină periodică, trebuie să testați acest lucru în carcasa Django pentru a vă asigura că totul funcționează conform destinației:
$ ./manage.py shell În [1]: de la publisher.tasks importați send_view_count_report În [2]: send_view_count_report.delay ()
Sperăm că ați primit un raport minuțios în e-mail.
Să creați acum o sarcină periodică. Deschide quick_publisher / celery.py
și să înregistreze sarcinile periodice:
# quick_publisher / celery.py import os din telina Importul de telina din celery.schedules import crontab os.environ.setdefault ('DJANGO_SETTINGS_MODULE', 'quick_publisher.settings') app = : settings ') # Încărcați module de activitate din toate configurările de aplicații Django înregistrate. app.autodiscover_tasks () app.conf.beat_schedule = 'trimite-report-fiecare-un minut': 'task': 'publisher.tasks.send_view_count_report', 'schedule': crontab (minute = 0, oră = 0) 'dacă doriți să fie difuzat zilnic la miezul nopții,
Până acum, am creat un program care să conducă sarcina publisher.tasks.send_view_count_report
fiecare minut indicat de crontab ()
notaţie. De asemenea, puteți specifica diferitele programe de tip Celery Crontab.
Deschideți o altă consolă, activați mediul adecvat și porniți serviciul Celery Beat.
$ țelină -Un rapid_publisher beat
Slujba serviciului Beat este de a împinge sarcini în țelină în conformitate cu programul. Luați în considerare faptul că programul face send_view_count_report
sarcina execută fiecare minut în funcție de configurare. Este bine pentru testare, dar nu este recomandat pentru o aplicație web din lumea reală.
Sarcini sunt adesea folosite pentru a efectua operațiuni nesigure, operațiuni care depind de resurse externe sau care pot eșua cu ușurință din diferite motive. Iată o orientare pentru a le face mai fiabile:
Sper că acest lucru a fost un tutorial interesant pentru dvs. și o bună introducere în utilizarea de țelină cu Django.
Iată câteva concluzii pe care le putem trage:
țelină beat
serviciu.