Creé una aplicación principal de python, main.py, que invoco con uvicorn main.main --reload . Que por supuesto ejecuta el siguiente código...
if __name__ == '__main__': main() Esa parte de la aplicación se ejecuta constantemente, lee datos y los procesa hasta que la aplicación se cancela manualmente. Uso asyncio para ejecutar rutinas.
Tarea
Me gustaría crear un pequeño tablero html en él, que pueda mostrar los datos que se calculan constantemente.
Pregunta
¿Cómo puedo ejecutar estos cálculos en segundo plano de main.py y seguir implementando un panel/sitio web con fastapi y jinja2?
Lo que he logrado hasta ahora
Puedo ejecutar la aplicación principal sin ningún código fastapi. Y puedo ejecutar el tablero sin las tareas en segundo plano. Ambos funcionan bien de forma independiente. Pero fastapi no se ejecuta cuando agrego su código a la aplicación principal con el cálculo en segundo plano. (¡¿Cómo podría?!? Solo puedo invocar la aplicación principal o la aplicación fastapi).
Cualquier concepto arquitectónico es apreciado. Gracias.
Fastapi no se ejecuta porque no puede ser alcanzado por el intérprete de python hasta que complete sus cálculos. Debe iniciar su aplicación web independientemente del proceso principal, le recomiendo enfáticamente que use docker-compose . Tal como lo recomienda fastapi, debe usar Dramatiq o Celery para grandes tareas en segundo plano, o simplemente puede ejecutar un servicio separado en los servicios de redacción, por ejemplo:
# background.py if __name__ == '__main__': main() # main.py app = FastAPI()docker-compose.yml:
services: web-app-interface: command: uvicorn main.main ... my-daemon: command: python background.pyPuede hacer que se comuniquen con un intermediario de mensajes, como RabbitMQ, etc. Y nunca use multiprocesamiento con uvicorn, puede causar fugas en el proceso, porque uvicorn gobierna a sus propios trabajadores.
Un buen enfoque es usar el decorador on_event con startup . Lo único que queda es usar asyncio.create_task para invocar la tarea en segundo plano. Mientras no lo espere, no se bloqueará y, por lo tanto, fastapi/uvicorn puede continuar atendiendo cualquier solicitud http.
my_service = MyService() @app.on_event('startup') async def service_tasks_startup(): """Start all the non-blocking service tasks, which run in the background.""" asyncio.create_task(my_service.start_processing_data())Además, dicho esto, cualquier solicitud puede consumir los datos de este servicio en segundo plano.
@app.get("/") def root(): return my_service.value Piense en MyService como cualquier clase de su agrado. Consumo de Kafka, cálculos, etc. Por supuesto, el value es solo un atributo de ejemplo de Class MyService.