Las aplicaciones web generalmente comienzan siendo simples pero pueden llegar a ser bastante complejas, y la mayoría de ellas exceden rápidamente la responsabilidad de responder solo a las solicitudes HTTP.
Cuando eso sucede, uno debe hacer una distinción entre lo que tiene que suceder instantáneamente (generalmente en el ciclo de vida de la solicitud HTTP) y lo que puede suceder eventualmente. ¿Porqué es eso? Bueno, porque cuando tu aplicación se sobrecarga de tráfico, cosas simples como esta hacen la diferencia.
Las operaciones en una aplicación web se pueden clasificar como operaciones críticas o de tiempo de solicitud y tareas en segundo plano, las que ocurren fuera del tiempo de solicitud. Estos mapas a los descritos anteriormente:
Las operaciones de tiempo de solicitud se pueden realizar en un solo ciclo de solicitud / respuesta sin preocuparse de que la operación se agote o que el usuario pueda tener una mala experiencia. Los ejemplos comunes incluyen CRUD (Crear, Leer, Actualizar, Eliminar) operaciones de base de datos y administración de usuarios (rutinas de inicio de sesión / cierre de sesión).
Las tareas en segundo plano son diferentes, ya que suelen requerir bastante tiempo y son propensas a fallar, principalmente debido a dependencias externas. Algunos escenarios comunes entre aplicaciones web complejas incluyen:
Las tareas de fondo son el enfoque principal de este tutorial. El patrón de programación más común utilizado para este escenario es el Producer Consumer Architecture..
En términos simples, esta arquitectura se puede describir así:
Por lo general, los consumidores recuperan las tareas de la cola en una modalidad de primero en entrar, primero en salir (FIFO) o según sus prioridades. También se hace referencia a los consumidores como trabajadores, y ese es el término que usaremos en todo momento, ya que es consistente con la terminología utilizada por las tecnologías analizadas..
¿Qué tipo de tareas se pueden procesar en segundo plano? Tareas que:
El apio es la opción de facto para realizar el procesamiento de tareas en segundo plano en el ecosistema de Python / Django. Tiene una API simple y clara, y se integra perfectamente con Django. Soporta varias tecnologías para la cola de tareas y varios paradigmas para los trabajadores..
En este tutorial, vamos a crear una aplicación web de juguetes Django (que trata con escenarios del mundo real) que utiliza el procesamiento de tareas en segundo plano.
Suponiendo que ya está familiarizado con la administración de paquetes y los entornos virtuales de Python, instalemos Django:
$ pip instala Django
He decidido construir otra aplicación de blogging. El enfoque de la aplicación estará en la simplicidad. Un usuario puede simplemente crear una cuenta y, sin demasiados problemas, puede crear una publicación y publicarla en la plataforma..
Configurar el quick_publisher
Proyecto Django:
$ django-admin startproject quick_publisher
Empecemos la aplicación:
$ cd quick_publisher $ ./manage.py startapp main
Al iniciar un nuevo proyecto de Django, me gusta crear un principal
Aplicación que contiene, entre otras cosas, un modelo de usuario personalizado. Más a menudo que no, encuentro limitaciones del Django predeterminado Usuario
modelo. Tener una costumbre Usuario
El modelo nos da el beneficio de la flexibilidad..
# main / models.py de django.db importan modelos de django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, clase BaseUserManager UserAccountManager (BaseUsererManager): use_in_migrations = true def _create_user (self, email, password, ** extra_fields) no correo electrónico: aumentar ValueError ('Debe proporcionarse una dirección de correo electrónico') si no es contraseña: aumentar ValueError ('Debe proporcionarse contraseña') correo electrónico = self.normalize_email (email) usuario = self.model (correo electrónico = email, ** extra_fields) user.set_password (password) user.save (utilizando = self._db) return user def create_user (self, email = Ninguno, password = None, ** extra_fields): return self._create_user (email, password, ** extra_fields) def create_superuser (self, email, password, ** extra_fields): extra_fields ['is_staff'] = True extra_fields ['is_superuser'] = True return self._create_user (email, password, ** extra_fields) clase Usuario (AbstractBaseUser, PermissionsMixin): REQUIRED_FIELDS = [] USERNAME_FIELD = objetos 'email' = UserAccountManager () email = models.EmailField ('email', unique = True, blank = False, null = False) full_name = models.CharField ('nombre completo', blank = True, null = True, max_length = 400) is_staff = models.BooleanField ('estado del personal' , predeterminado = falso) is_active = models.BooleanField ('activo', predeterminado = verdadero) def get_short_name (self): return self.email def get_full_name (self): return self.email def __unicode __ (self): return self.email
Asegúrese de consultar la documentación de Django si no está familiarizado con el funcionamiento de los modelos de usuario personalizados..
Ahora debemos decirle a Django que use este modelo de usuario en lugar del modelo predeterminado. Añade esta línea a la quick_publisher / settings.py
expediente:
AUTH_USER_MODEL = 'main.User'
También necesitamos agregar el principal
aplicación a la INSTALLED_APPS
lista en el quick_publisher / settings.py
expediente. Ahora podemos crear las migraciones, aplicarlas y crear un superusuario para poder iniciar sesión en el panel de administración de Django:
$ ./manage.py makemigrations main $ ./manage.py migrate $ ./manage.py creasupplususuario
Ahora vamos a crear una aplicación de Django separada que sea responsable de las publicaciones:
$ ./manage.py startapp publish
Vamos a definir un modelo de post simple en editor / modelos.py
:
desde django.db importar modelos desde django.utils importar zona horaria desde django.contrib.auth importar get_user_model class Post (models.Model): author = models.ForeignKey (get_user_model ()) created = models.DateTimeField ('Created Date', por defecto = timezone.now) title = models.CharField ('Title', max_length = 200) content = models.TextField ('Content') slug = models.SlugField ('Slug') def __str __ (self): return '"% s "por% s '% (self.title, self.autor)
Enganchando el Enviar
modelo con el administrador de Django se realiza en el editor / admin.py
archivo como este:
desde django.contrib importar administrador desde .models importar Post @ admin.register (Post) clase PostAdmin (admin.ModelAdmin): pasar
Finalmente, enganchemos el editor
aplicación con nuestro proyecto añadiéndolo a la INSTALLED_APPS
lista.
Ahora podemos ejecutar el servidor y dirigirnos a http: // localhost: 8000 / admin /
y crea nuestras primeras publicaciones para que tengamos algo con lo que jugar:
$ ./manage.py runserver
Confío en que hayas hecho tu tarea y hayas creado las publicaciones..
Vamonos. El siguiente paso obvio es crear una forma de ver las publicaciones publicadas..
# publisher / views.py from django.http import Http404 desde django.shortcuts import render desde .models import Post def view_post (request, slug): try: post = Post.objects.get (slug = slug) excepto Post.DoesNotExist: raise Http404 ("Poll no existe") return render (request, 'post.html', context = 'post': post)
Asociemos nuestra nueva vista con una URL en: quick_publisher / urls.py
# quick_publisher / urls.py desde django.conf.urls importar url desde django.contrib importar administrador desde publisher.views importar view_post urlpatterns = [url (r '^ admin /', admin.site.urls), url (r '^ (?PAG[a-zA-Z0-9 \ -] +) ', view_post, name = "view_post")]
Finalmente, vamos a crear la plantilla que representa la publicación en: editor / plantillas / post.html
título de la entrada
Publicar Contenido
Publicado por post.author.full_name en post.created
Ahora podemos dirigirnos a http: // localhost: 8000 / the-slug-of-the-post-you-created / en el navegador. No es exactamente un milagro del diseño web, pero hacer publicaciones atractivas está fuera del alcance de este tutorial..
Aquí está el escenario clásico:
Vamos a añadir un es verificado
bandera y la verificación_uuid
sobre el Usuario
modelo:
# main / models.py importa uuid class User (AbstractBaseUser, PermissionsMixin): REQUIRED_FIELDS = [] USERNAME_FIELD = 'email' objects = UserAccountManager () email = models.EmailField ('email', unique = blank, blank = False, null = Falso) full_name = models.CharField ('nombre completo', blank = True, null = True, max_length = 400) is_staff = models.BooleanField ('estado del personal', predeterminado = False) is_active = models.BooleanField ('active', default = True) is_verified = models.BooleanField ('verified', default = False) # Agregue el indicador 'is_verified' verified_uuid = modelsIDIDField ('Unique UUID de verificación', default = uuid.uuid4) def get_short_name (self): return self.email def get_full_name (self): devolver self.email def __unicode __ (self): devolver self.email
Aprovechemos esta ocasión para agregar el modelo de usuario al administrador:
desde django.contrib importar administrador desde .models importar Usuario @ admin.register (Usuario) clase UserAdmin (admin.ModelAdmin): pasar
Hagamos que los cambios se reflejen en la base de datos:
$ ./manage.py makemigrations $ ./manage.py migrate
Ahora necesitamos escribir un fragmento de código que envíe un correo electrónico cuando se crea una instancia de usuario. Para esto son las señales de Django, y esta es una ocasión perfecta para tocar este tema..
Las señales se activan antes / después de que ocurran ciertos eventos en la aplicación. Podemos definir funciones de devolución de llamada que se activan automáticamente cuando se disparan las señales. Para hacer un activador de devolución de llamada, primero debemos conectarlo a una señal.
Vamos a crear una devolución de llamada que se activará después de que se haya creado un modelo de usuario. Añadiremos este código después de la Usuario
definición del modelo en: main / models.py
desde django.db.models importar señales desde django.core.mail importar send_mail def user_post_save (remitente, instancia, señal, * args, ** kwargs): si no es instance.is_verified: # Enviar correo electrónico de verificación send_mail ('Verifique su cuenta de QuickPublisher ',' Siga este enlace para verificar su cuenta: "http: // localhost: 8000% s '% reverse (' verificar ', kwargs = ' uuid ': str (instance.verification_uuid)),' desde @ quickpublisher. dev ', [instance.email], fail_silently = False), signal.post_save.connect (user_post_save, sender = User)
Lo que hemos hecho aquí es que hemos definido una user_post_save
función y lo conectó a la post_save
señal (una que se activa después de que se haya guardado un modelo) enviada por el Usuario
modelo.
Django no solo envía correos electrónicos por sí solo; Debe estar vinculado a un servicio de correo electrónico. En aras de la simplicidad, puede agregar sus credenciales de Gmail en quick_publisher / settings.py
, o puede agregar su proveedor de correo electrónico favorito.
Así es como se ve la configuración de Gmail:
EMAIL_USE_TLS = Verdadero EMAIL_HOST = 'smtp.gmail.com' EMAIL_HOST_USER = '@ gmail.com 'EMAIL_HOST_PASSWORD =' 'EMAIL_PORT = 587
Para probar las cosas, vaya al panel de administración y cree un nuevo usuario con una dirección de correo electrónico válida que pueda verificar rápidamente. Si todo salió bien, recibirás un correo electrónico con un enlace de verificación. La rutina de verificación aún no está lista..
Aquí está cómo verificar la cuenta:
# main / views.py from django.http import Http404 from django.shortcuts import render, redirect desde .models import User def home (solicitud): return render (request, 'home.html') def verificar (request, uuid): try: user = User.objects.get (verified_uuid = uuid, is_verified = False) excepto User.DoesNotExist: raise Http404 ("El usuario no existe o ya está verificado") user.is_verified = True user.save () return redirect ( 'casa')
Enganche las vistas en: quick_publisher / urls.py
# quick_publisher / urls.py desde django.conf.urls importar url desde django.contrib importar administrador desde publisher.views importar view_post desde main.views importar inicio, verificar urlpatterns = [url (r '^ $', inicio, nombre = " inicio "), url (r '^ admin /', admin.site.urls), url (r '^ verificar / (? P[a-z0-9 \ -] +) / ', verificar, nombre = "verificar"), url (r' ^ (? P [a-zA-Z0-9 \ -] +) ', view_post, name = "view_post")]
Además, recuerda crear un home.html
archivar bajo main / templates / home.html
. Será rendido por el casa
ver.
Intenta ejecutar todo el escenario de nuevo. Si todo está bien, recibirá un correo electrónico con una URL de verificación válida. Si sigue la URL y luego ingresa al administrador, puede ver cómo se verificó la cuenta.
Aquí está el problema con lo que hemos hecho hasta ahora. Quizás hayas notado que crear un usuario es un poco lento. Eso es porque Django envía el correo electrónico de verificación dentro del tiempo de solicitud.
Así es como funciona: enviamos los datos del usuario a la aplicación Django. La aplicación crea un Usuario
modela y luego crea una conexión a Gmail (u otro servicio que hayas seleccionado). Django espera la respuesta, y solo entonces devuelve una respuesta a nuestro navegador..
Aquí es donde entra el apio. Primero, asegúrese de que esté instalado:
$ pip instala apio
Ahora necesitamos crear una aplicación de apio en nuestra aplicación Django:
# quick_publisher / celery.py import os desde celery import Celery os.environ.setdefault ('DJANGO_SETTINGS_MODULE', 'quick_publisher.settings') app = Celery ('quick_publisher') app.config_from_object ('django.conf settings) Módulos de tareas de todas las configuraciones de aplicaciones Django registradas. app.autodiscover_tasks ()
El apio es una cola de tareas. Recibe tareas de nuestra aplicación Django y las ejecutará en segundo plano. El apio debe combinarse con otros servicios que actúan como intermediarios.
Los intermediarios intermedian el envío de mensajes entre la aplicación web y el apio. En este tutorial, estaremos usando Redis. Redis es fácil de instalar y podemos comenzar a utilizarlo fácilmente sin demasiados problemas..
Puede instalar Redis siguiendo las instrucciones en la página de Inicio rápido de Redis. Tendrá que instalar la biblioteca Redis Python, pip instalar redis
, y el paquete necesario para usar Redis y Apio: pip instalar apio [redis]
.
Inicie el servidor Redis en una consola separada como esta: $ redis-server
Agreguemos las configuraciones relacionadas con Apio / Redis en quick_publisher / settings.py
:
# REDIS ajustes relacionados REDIS_HOST = 'localhost' REDIS_PORT = '6379' BROKER_URL = 'redis: //' + REDIS_HOST + ':' + REDIS_PORT + '/ 0' BROKER_TRANSPORT_OPTIONS = 'visibilidad_timeout': 3600 CELERY_RESULTADO_BACKEND_BACKEND = Redis_PRODUCTUM_BACKEND_BACKEND = '' / '+ REDIS_HOST +': '+ REDIS_PORT +' / 0 '
Antes de que se pueda ejecutar algo en el apio, debe declararse como una tarea..
Aquí está cómo hacer esto:
# main / tasks.py importar el registro desde django.urls importar inverso desde django.core.mail importar send_mail desde django.contrib.auth importar get_user_model desde quick_publisher.celery importar app @ app.task def send_verification_email (user_id): UserModel = get_user_model ( ) intente: usuario = UserModel.objects.get (pk = user_id) send_mail ('Verifique su cuenta de QuickPublisher', 'Siga este enlace para verificar su cuenta: "http: // localhost: 8000% s'% reverse ('verificar' , kwargs = 'uuid': str (user.verification_uuid)), '[email protected]', [user.email], fail_silently = False,) excepto UserModel.DoesNotExist: logging.warning ("Se intentó enviar la verificación correo electrónico al usuario no existente '% s' "% user_id)
Lo que hemos hecho aquí es esto: movimos la funcionalidad de correo electrónico de verificación de envío a otro archivo llamado tareas.py
.
Algunas notas:
INSTALLED_APPS
y registra las tareas en tareas.py
archivos.Envia un correo electronico de verificación
funcionar con @ app.task
. Esto le dice a Celery que esta es una tarea que se ejecutará en la cola de tareas.user_id
preferible a Usuario
objeto. Esto se debe a que podemos tener problemas para serializar objetos complejos al enviar las tareas a Celery. Es mejor mantenerlos simples..Volviendo a main / models.py
, El código de señal se convierte en:
desde django.db.models importar señales de main.tasks import send_verification_email def user_post_save (remitente, instancia, señal, * args, ** kwargs): si no es instance.is_verified: # Enviar señales de verificación enviar_verificación_email.delay (instance.pk) señales .post_save.connect (user_post_save, sender = User)
Observe cómo llamamos al .retrasar
Método en el objeto de tarea. Esto significa que estamos enviando la tarea a Celery y no esperamos el resultado. Si usamos send_verification_email (instance.pk)
En cambio, seguiríamos enviándolo a Celery, pero estaríamos esperando que la tarea termine, lo cual no es lo que queremos..
Antes de comenzar a crear un nuevo usuario, hay un problema. El apio es un servicio, y tenemos que iniciarlo. Abra una nueva consola, asegúrese de activar el virtualenv apropiado y navegue a la carpeta del proyecto.
$ celery worker -A quick_publisher --loglevel = debug --concurrency = 4
Esto da inicio a cuatro trabajadores del proceso de Apio. Sí, ahora ya puedes ir y crear otro usuario. Observe cómo no hay demora, y asegúrese de ver los registros en la consola de Celery y ver si las tareas se ejecutan correctamente. Esto debería verse algo como esto:
[2017-04-28 15: 00: 09,190: DEBUG / Proceso principal] Tarea aceptada: main.tasks.send_verification_email [f1f41e1f-ca39-43d2-a37d-9de085dc99de] pid: 62065 [2017-04-28 15: 00: 11,740: INFO / PoolWorker-2] Tarea main.tasks.send_verification_email [f1f41e1f-ca39-43d2-a37d-9de085dc99de] se realizó con éxito en 2.5500912349671125s: Ninguno
Aquí hay otro escenario común. La mayoría de las aplicaciones web maduras envían a sus usuarios correos electrónicos de ciclo de vida para mantenerlos comprometidos. Algunos ejemplos comunes de correos electrónicos de ciclo de vida:
Esto es lo que vamos a hacer en nuestra aplicación. Vamos a contar cuántas veces se ha visto cada publicación y enviar un informe diario al autor. Una vez cada día, vamos a revisar todos los usuarios, buscar sus publicaciones y enviar un correo electrónico con una tabla con las publicaciones y los recuentos de visitas..
Vamos a cambiar el Enviar
Modelo para que podamos acomodar el escenario de conteos de vistas..
clase Post (models.Model): author = models.ForeignKey (Usuario) created = models.DateTimeField ('Date Date', default = timezone.now) title = models.CharField ('Title', max_length = 200) content = models .TextField ('Contenido') slug = models.SlugField ('Slug') view_count = models.IntegerField ("View Count", default = 0) def __str __ (self): return '"% s" by% s'% ( título propio, autor propio
Como siempre, cuando cambiamos un modelo, necesitamos migrar la base de datos:
$ ./manage.py makemigrations $ ./manage.py migrate
También modifiquemos el ver publicacion
Django vista para contar vistas:
def view_post (request, slug): try: post = Post.objects.get (slug = slug) excepto Post.DoesNotExist: raise Http404 ("Poll no existe") post.view_count + = 1 post.save () retorno render (solicitud, 'post.html', context = 'post': post)
Sería útil mostrar el Visto post.view_count vecesconteo de visitas
en la plantilla Agrega esto
en algún lugar dentro del editor / plantillas / post.html
expediente. Haga algunas vistas en una publicación ahora y vea cómo aumenta el contador.
Vamos a crear una tarea de apio. Ya que se trata de posts, lo voy a colocar en editor / tareas.py
:
desde django.template importar plantilla, contexto desde django.core.mail importar send_mail desde django.contrib.auth importar get_user_model desde quick_publisher.celery importar aplicación desde publisher.models importar Publicar REPORT_TEMPLATE = "" Así es como lo hizo ahora: % para la publicación en las publicaciones% "post.title": vista post.view_count veces | % endfor% "" "@ app.task def send_view_count_report (): para el usuario en get_user_model (). objects.all (): posts = Post.objects.filter (author = user) si no es posts: continue template = Template (REPORT_TEMPLATE) send_mail ('Your QuickPublisher Activity', template.render (context = Context ('posts': publicaciones)), '[email protected]', [user.email], fail_silently = False,)
Cada vez que realice cambios en las tareas de apio, recuerde reiniciar el proceso de apio. El apio necesita descubrir y recargar las tareas. Antes de crear una tarea periódica, deberíamos probar esto en el shell de Django para asegurarnos de que todo funciona como debe:
$ ./manage.py shell En [1]: desde publisher.tasks import send_view_count_report In [2]: send_view_count_report.delay ()
Esperemos que hayas recibido un pequeño e ingenioso informe en tu correo electrónico..
Ahora vamos a crear una tarea periódica. Abrir quick_publisher / celery.py
y registrar las tareas periódicas:
# quick_publisher / celery.py import os del celery import Celery from celery.schedules import crontab os.environ.setdefault ('DJANGO_SETTINGS_MODULE', 'quick_publisher.settings') app = Celery ('quick_publisher') app.config_fu_fop / : settings ') # Cargar módulos de tareas de todas las configuraciones de aplicaciones Django registradas. app.autodiscover_tasks () app.conf.beat_schedule = 'send-report-every-single-minute': 'task': 'publisher.tasks.send_view_count_report', 'schedule': crontab (), # change to 'crontab (minuto = 0, hora = 0) 'si desea que se ejecute diariamente a la medianoche,
Hasta ahora, hemos creado un calendario que ejecutaría la tarea publisher.tasks.send_view_count_report
cada minuto indicado por el crontab ()
notación. También puede especificar varios horarios de apio Crontab.
Abra otra consola, active el entorno adecuado e inicie el servicio Celery Beat.
$ apio - Un golpe rápido de editor
El trabajo del servicio Beat es impulsar las tareas en el apio de acuerdo con el calendario. Ten en cuenta que el horario hace que el send_view_count_report
Tarea ejecutada cada minuto según la configuración. Es bueno para las pruebas, pero no se recomienda para una aplicación web del mundo real..
Las tareas a menudo se utilizan para realizar operaciones no confiables, operaciones que dependen de recursos externos o que pueden fallar fácilmente debido a varias razones. Aquí hay una guía para hacerlos más confiables:
Espero que haya sido un tutorial interesante para ti y una buena introducción al uso de Celery con Django..
Aquí hay algunas conclusiones que podemos sacar:
batido de apio
Servicio.