Hacer Deploy de FastAPI y Celery (con RabbitMQ y PostgreSQL) en Heroku
Tags: heroku, fastapi, rabbitmq, celery
Published: 2022-07-09 22:05:02

Como ya es conocido por todos, plataformas como Heroku y Netlify nos permiten desplegar nuestros proyectos y páginas web sin costo alguno. El día de hoy haremos uso de Heroku para desplegar un [proyecto](https://letbxl.herokuapp.com/docs "Visitar DOCS del proyecto") de Scraping hecho con FastAPI.
<!--more-->

![DOCS](https://res.cloudinary.com/rooyca/image/upload/v1656265660/Blog/Imgs/Celery%2C%20FastAPI%20en%20Heroku/Screenshot_at_2022-06-26_12-47-22_zk3uxw.png)

# Heroku

[Heroku](https://heroku.com/) es una Plataforma como Servicio (PaaS) que le permite a los desarrollados construir, ejecutar y operar aplicaciones enteramente en la Nube.

Heroku cuenta (a la fecha) con cuatro planes:
- Gratis
	- Aplicaciones no comerciales, pruebas de concepto y proyectos personales.
- Producción ($25 o más)
	- Aplicaciones de negocios, tales como Aplicaciones y Apis internas de empresas.
- Avanzado ($250 o más)
	- Aplicaciones con funcionalidades complejas que requieren alta disponibilidad, muy poca latencia y manejo de altos volúmenes de peticiones.
- Empresa (Precio por definir)
	- Aplicaciones de organizaciones a gran escala.


*Más [información](heroku.com/pricing)*

# Scraping con FastAPI

Uno de los problemas que encontré a lo largo del desarrollo de este proyecto fue el de los tiempos de respuesta. Cuando se hace una petición a la API para que obtenga la información de una lista de Letterboxd, dicho proceso puede tomar algunos segundos, incluso minutos. El problema radica en que el servidor se queda "pegado" tratando de responder a la solicitud por lo que no es posible realizar una nueva petición. Como podrán imaginar, esperar hasta UN minuto por solicitud no es nada practico ni agradable, por lo que aquí es donde entra Celery y RabbitMQ.

![SynYAsync](https://res.cloudinary.com/rooyca/image/upload/v1656267002/Blog/Imgs/Celery%2C%20FastAPI%20en%20Heroku/asyncVsyncD_wbhy05.png)

En la pagina oficial de [Celery](https://docs.celeryq.dev/en/stable/) encontramos lo siguiente:

> Celery is a simple, flexible, and reliable distributed system to process vast amounts of messages, while providing operations with the tools required to maintain such a system. It’s a task queue with focus on real-time processing, while also supporting task scheduling.

Y en la página de [RabbitMQ](https://www.rabbitmq.com/):

> RabbitMQ is a messaging broker - an intermediary for messaging. It gives your applications a common platform to send and receive messages, and your messages a safe place to live until received.

En pocas palabras, y con posibilidades de caer en equivocaciones, podríamos decir que Celery se encarga del manejo de las solicitudes para que estas puedan procesarse en "segundo plano" y así liberar el End Point para continuar recibiendo peticiones. Esto se logra -a muy grandes rasgos- asignándole una ID a la tarea, esta ID es la respuesta de esa primera petición. Con dicha ID podemos, en otro End Point, estar pendientes de nuestra tarea, cuando ya esté lista recibiremos el JSON de la lista de películas.

![Response](https://res.cloudinary.com/rooyca/image/upload/v1656264521/Blog/Imgs/Celery%2C%20FastAPI%20en%20Heroku/Screenshot_at_2022-06-26_12-28-21_ooxezx.png)

Ahora bien, entonces ¿cuál es la función de RabbitMQ en todo esto? 

"Sencillo", RabbitMQ lo que hace es almacenar la solicitud y la respuesta que genera Celery.

**De las dos definiciones anteriores ruego se tome en serio muy poco de lo expuesto y en lugar aconsejo que se haga una investigación por cuenta propia.**

## ¿Dónde correr RabbitMQ?

![Rabbit](https://res.cloudinary.com/rooyca/image/upload/c_scale,w_400,z_1.5/v1656263933/Blog/Imgs/Celery%2C%20FastAPI%20en%20Heroku/RabbitMQ_logo_njq6vk.svg)

Para esto encontré dos opciones: 

1. Google Cloud o cualquier plataforma similar (AWZ, Azure, etc.)
2. [CloudAMQP](https://devcenter.heroku.com/articles/cloudamqp)

En este caso vamos a utilizar la primer opción, pero si deseas probar la segunda puedes dar click en el enlace de arriba y encontrarás una guía completa.

La pregunta es, ¿cómo hice para correr RabbitMQ con Google CLoud? Fácil, hice uso de Contenedores. Primero busqué la imagen de RabbitMQ para posteriormente correrla... todo muy sencillo hasta ahí.

El problema vino a la hora de tratar de obtener una IP externa. Lo que hice fue lo siguiente. En la consola de Google Cloud corrí:

(**Remplazando todo lo que está completamente en Mayúsculas**) 

```gcloud container clusters get-credentials [NOMBRE_CLUSTER] --zone [ZONA_CLUSTER]```

Después, para exponer el servicio:
```
kubectl patch service/SERVICO_NOMBRE-rabbitmq-svc \
  --namespace default \
  --patch '{"spec": {"type": "LoadBalancer"}}'

```

Por último, para obtener la IP externa y los puertos:

```kubectl get service SERVICO_NOMBRE-rabbitmq-svc```


## ¿Cómo correr Celery en Heroku?

![Celery](https://res.cloudinary.com/rooyca/image/upload/c_scale,w_102/v1656263916/Blog/Imgs/Celery%2C%20FastAPI%20en%20Heroku/celery_512_n1acnv.png)
Como sabrán, a la hora de correr un APP en Heroku necesitamos un archivo llamado *Procfile* en el que le especificamos a Heroku los comando que se ejecutarán a la hora de iniciar la aplicación.

Entonces, para poder ejecutar FastAPI y Celery a la vez necesitamos que nuestro archivo *Procfile* luzca algo así:

```
web: gunicorn -w 3 -k uvicorn.workers.UvicornWorker main:app
worker: celery -A main.celery worker -Q letterboxList

``` 

- Donde *main* hace referencia al nombre del archivo principal.
- *app y celery* hacen referencia al nombre de las variables dentro del *main* archivo que se encargan de iniciar FastAPI y Celery, respectivamente.
- Por último, *letterboxList* hace referencia al nombre de la tarea.

Ya para finalizar, en mi caso tuve que realizar un paso más. 

Tuve que dirigirme a la página de [Heroku](https://heroku.com), seleccionar el APP que había creado, darle en *Resources* y revisar que los dos Dynos estuvieran activados. En mi caso el de *worker* no lo estaba así que tuve que darle al icono del Lápiz (la opción de editar Dyno) y activarlo.

![OneMore](https://res.cloudinary.com/rooyca/image/upload/v1656264842/Blog/Imgs/Celery%2C%20FastAPI%20en%20Heroku/Screenshot_at_2022-06-26_12-32-37_llrzqi.png)

Y eso sería todo, así de sencillo es correr FastAPI y Celery en Heroku. Por último, si desean ver el resultado final de este proyecto pueden dar click [aquí](https://letbxl.herokuapp.com/docs). 

Recuerden que si tienen alguna duda pueden escribirme por: 

-    Discord: rooyca#6075
-    Telegram: @seiseiseis

Hasta la próxima. Que tengan un excelente día.