Celery es una aplicación que nos permite crear tareas de trabajo asíncronas gestionadas por un gestor de colas que está basada en el envío de mensajes de manera distribuida. Se focaliza en operaciones en tiempo real pero también soporta la calendarización de tareas, es decir, puede lanzar tareas que se tengan que ejecutar en un momento determinado o de manera periódica. Las unidades de ejecución, llamadas tareas, se ejecutan de manera concurrente en uno o más nodos de trabajo. Estas tareas pueden ejecutarse de manera asíncrona bien de manera síncrona (esperando hasta que la tarea está lista). El sistema de mensajería recomendado por Celery es RabbitMQ aunque es bastante agnóstico en el tema y puede usar como sustituto Redis, MongoDB o una base de datos sql. Aunque el programa surgió ligado a Django, actualmente es una librería que se puede utilizar de manera independiente. Se han creado dos proyectos a partir del original, Celery. que mantiene la estructura básica de la librería y se puede utilizar desde cualquier aplicación Python y django-celery que se encarga de la integración con Django.

Bien, hasta aquí la parrafada de introducción traducida con más o menos fortuna de la documentación de Celery,  lo más imporante  es que Celery nos permite ejecutar tareas de manera distribuida, donde el director de orquesta (si seguimos las recomendaciones) es una aplicación llamada RabbitMQ, escrita en Erlang, por cierto, que se encarga de recibir las órdenes de las tareas que se tienen que ejecutar y las distribuye entre los distintos nodos que están registrados para ejecutar dicha tarea. Este director es el que se encarga de saber como está cada tarea y de obtener el resultado cuando esta finaliza, de forma que el programa que ha lanzado la tarea puede esperar a obtener el resultado,  simplemente lanzar la tarea y olvidarse o utilizar un callback para ejectuar una acción cuando el resultado esté disponible. Así pués tenemos que distinguir tres actores en todo esto, la aplicación que encarga la tarea, la aplicación que ejecuta la tarea (el worker) y la aplicación que se encarga de la comunicación entre quien encarga la tarea y quien la ejecuto. La gracia de todo esto es que podemos tener más de un worker en distintas máquinas o en la misma máquina, de tal manera que el gestor se encarga de repartir las tareas en función de quien está libre para ejecutarlas. Pensamos un nomento en el significado de esto:  podemos pasar de tener una aplicación que lo hace todo, a un conjunto de aplicaciones que pueden estar en distintas máquinas que colaboran entre sí. Resumiendo: nos permite la escalabilitat horizontal.

Por qué usar Celery?

Antes de Celery había trabajado con Beanstalkd, un sistema parecido, muy simple y rápido. Permitidme contar aquí una batalleia, trabjando de jefe de proyecto web para Tui España, uno de los proyectos en los que participé consistía en creación de un servicio web para la venta on-line de transfers y excursiones. 

La información estaba (y supongo que todavía sigue) en una base de datos Oracle y con una estructura pensada para introducir información, de forma que hacer una consulta un poco compleja cómo "quiero todas las excursiones que hay tal día que se pueden hacer desde tal punto" era extremadamente lento. La solución que encontramos fue desnormalitzar la información pasándola hacia una base de datos Postgresql, unos cuantos de millones de registros que se actualizaban diariamente. La primera versión del programa de carga estaba hecho en Java, fue compleja de crear, casi 3 meses de trabajo, mala de mantener y ejecutándolo todo con hilos necesitaba más de 2 horas para hacer la carga. La segunda versión la hicimos con Python, una semana y media, un 10% del código, más funcionalidad y utilizaremos Beanstalk para distribuir las tareas, puesto que la carga en sí se podía particionar muy bien.

Esto nos permitió aprovechar la potencia de servidores que estaban ociosos y aprovechar también los tiempos muertos entre la consulta a Oracle, el parseo de la respuesta y la introducción a Postgres. En una de las pruebas pondremos en marcha procesos en 4 servidores diferentes, con un total de unos 20 workers y un tiempo de carga de unos 3 minutos. Con 2 servidores el tiempo era de unos 5 minutos, una mejora muy significativa respecto a la aplicación Java.

Así pues una de las utilidad que tenemos es la de poder distribuir tareas muy pesadas entre muchas máquinas. Celery puede realizarlas mismas tareas qeu Beanstalk sólo que además cuenta con una documentación extensa, sistemas de monitoritzación y gestión y en el caso de utilizar Django, la posibilidad de mantener las tareas dentro del mismo proyecto. El segundo uso importante de Celery es la web. En aplicaciones web el que queremos es dar la mejor experiencia de usuario posible, y esta experiencia se degrada si el usuario tiene que esperar mucho. 

Imaginemos por ejemplo una aplicación de comercio electrónico donde el usuario pide una factura y esta le llega en pdf. En sistemas de mucha carga la generación del pdf puede degradar la respuesta global de la web, lo interesante es poder enviar la petición de factura a un sistema externo a la web y que sea este el encargado de la generación y el envío. Este sistema no tiene porque estar ni siquiera en la misma máquina donde está la aplicación web o bien pueden aprovecharse horas de poca carga para hacer la generación y el envío. Ir hacia la complejidad de arquitectura que representa añadir un sistema de colas asíncrono a nuestra aplicación implica querer ir hacia un aprovechamiento de los recursos y sobre todo entender cómo funciona nuestra aplicación, que se tiene que hacer de manera inmediata y que se puede hacer de manera asíncrona, saber cuáles son las tareas más pesadas que nos afectan y como podemos paralelizarlas y distribuirlas. 

Otra solución es poner más máquina con n-mil cores y pasar de todo, pero además de ser una opción económicamente más cara y técnicamente discutible, es una opción a corto plazo, puesto que si nuestra aplicación tiene mucho éxito puede llegar un momento donde ya ni haya máquinas más grandes o el coste de las mismas se coma todo nuestros beneficios. Podemos acabar trabajando para el fabricante de hardware y para pagar las licencias asociadas. Convencidos? Espero que sí. Un sistema así no es apto para todas las aplicaciones, la complejidad de instalación y mantenimiento es más grande que tener todo el sistema en una máquina y sin distribuir, pero si tenéis necesidades de hacer cálculos intensivos de manera puntual o tareas por las cuales no queréis hacer esperar el usuario Celery es una muy buena solución.

Además si nuestra aplicación está en la nube (Amazon por ejemplo), puntualmente podemos levantar tantas instancias como haga falta y ejecutar workers para ejecutar las tareas más pesadas. O  un Datacenter propio donde la mayoría de servidores están ociosos, podemos instalar workers en las màquienas con menos carga para que ayuden en los procesos pesados. Se nos abre un abanico de posibilidad prácticamente ilimitado.