Desarrollo de tareas asíncronas en Celery

Jose Armando Rivera Ramirez
NinjaCom
Published in
4 min readMay 30, 2020

Si has utilizado python en algun momento y tuviste que enviar correos, consumido alguna API de 3ros o generado algún PDF habrás notado que son tareas que tienen una larga duración por lo cual si necesitas enviar alguna alerta o notificación después que la tarea se complete el proceso será muy lento ya que debe esperar a que la tarea anterior sea completada.

Imagen obtenida de https://docs.celeryproject.org/

Celery es un sistema distribuido para procesar grandes cantidades de mensajes, al tiempo que proporciona las herramientas necesarias para mantener dicho sistema. Utiliza colas de tareas para distribuir el trabajo en hilos o maquinas dependiendo si se usa localmente o de forma distribuida, sin embargo para que celery funcione requiere de un broker (intermediario), el cual se encargara de mediar la comunicación entre el cliente y los workers, dichos brokers se encargan de entregar el mensaje del cliente a los workers mientras celery supervisa que no haya tareas pendientes por realizar.

Celery fue escrito en python pero actualmente cuenta con una version para php y nodejs usando javascript o typescript, sin embargo tambien es posible crear un REST API o SOAP y simplemente consumirlo con cualquier lenguaje, como se mencionó previamente es necesario utilizar un broker que sirva de intermediario entre los mensajes y los worker entre los más populares se encuentran RabbitMQ y Redis cada uno de ellos cuenta con pros y contras probablemente te preguntes ¿Entonces cuál de ellos debo de usar? la respuesta depende mucho acerca de tu arquitectura sin embargo algunas de las consideraciones más importantes es saber si requieres persistencia, si en algún momento tendrá que escalar y el número de consumidores con los cuales contaras.

Particularmente la configuración inicial de redis es muy rápida y simple sin embargo lo cual es lo contrario a rabbitmq ya que requiere tener más conocimiento, redis es mucho más rápido que rabbitmq sin embargo puede que exista una pérdida de información por lo cual no se debe usar en tareas críticas, todo lo contrario, a rabbitmq que a pesar de no ser tan rápido cuenta con mayor fiabilidad, así como la posibilidad de enrutamiento avanzado.

Diagrama de funcionamiento

Un ejemplo de cómo funciona su arquitectura se encuentra en la imagen de arriba en donde un cliente desde una aplicación envía un correo posiblemente, en la interfaz de usuario se le envía una notificación indicando que su correo ha sido enviado, al mismo tiempo la tarea es enviada a algún worker que se encuentre disponible en ese momento para procesar el envió así mismo si se requiere guardar el resultado en una base de datos es posible sin embargo no es obligatorio todo depende de la arquitectura de la aplicación.

Celery es fácil de implementar con muchos frameworks web algunos de ellos incluso cuentan con un paquete de integración, no es necesario integrarlos, pero facilita el desarrollo.

Imagen obtenida de https://docs.celeryproject.org/

Algunas buenas prácticas o tips

Para finalizar creo que es importante mencionar consejos con los cuales puede incrementar el rendimiento o simplemente evitar caer en errores comunes

  1. Siempre mantén el número de concurrencia cercano a la cantidad de núcleos de CPU, esto ayuda a maximizar el rendimiento sin abusar de los recursos disponibles.
  2. Si no te importan los resultados de una tarea asegúrate de indicarlo a celery con la opción ignore_result ya que al almacenarlos se pierde tiempo y recursos.
  3. Evita el lanzamiento de subtareas síncronas ya que esto puede ser un cuello de botella para los workers.
  4. Es mejor dividir el problema en muchas tareas pequeñas en lugar de tener unas pocas tareas de larga duración.
  5. El worker que se encargara de la tarea debe estar lo más cercano a los datos por lo cual si los datos están muy lejos es mejor usar un worker que se encuentre ahí.
  6. Si no es posible ejecutar un worker lo más cercano a los datos es recomendable usar un sistema de caché distribuido, como memcached.
  7. Los objetos de un modelo no deben ser pasados como argumentos a las tareas ya que es posible que una tarea que se ejecute antes cambie el estado de dicho objeto.
  8. Siempre realiza pruebas para asegurarte que tu aplicación funciona como se espera.
  9. Si es necesario hacer debug tu código puedes usar set_trace() para detener la tarea en algún punto y depurarla remotamente en otro lugar.

Fuentes:

https://docs.celeryproject.org/en/stable/
http://emmanuel-klinger.net/distributed-task-queues-for-machine-learning-in-python-celery-rabbitmq-redis.html

--

--