Estoy trabajando en una API web de Python usando FastAPI
.
Actualmente es una aplicación muy pequeña, pero en los próximos meses espero que tenga algunos cientos de routes
y services
.
Este es un ejemplo de una route
que tengo:
my_service_1 = MyService1() my_service_2 = MyService2() @router.get("/route-1") def route_1(): # Do something with the services @router.get("/route-2") def route_2(): # Do something with the services @router.get("/route-3") def route_3(): # Do something with the services
Dejé la declaración de servicios en la parte superior del archivo para mayor comodidad, por lo que todas las rutas en el archivo pueden usarlas fácilmente.
Mi pregunta es, con cientos de archivos como este, tan pronto como se inicie la aplicación, se registrarán todas las rutas, por lo tanto, se instanciarán todos los servicios. ¿Es una mala idea hacerlo? ¿Causaría demasiado uso de memoria dado que todos los servicios ahora están cargados en la memoria?
¿Sería mejor algo como esto en términos de rendimiento/uso de memoria?
@router.get("/route-1") def route_1(): my_service_1 = MyService1() my_service_2 = MyService2() # Do something with the services @router.get("/route-2") def route_2(): my_service_1 = MyService1() my_service_2 = MyService2() # Do something with the services @router.get("/route-3") def route_3(): my_service_1 = MyService1() my_service_2 = MyService2() # Do something with the services
Por favor, perdóname si esta no es una forma pitónica de hacer las cosas, vengo de un entorno de C#.
TL;DR
Crear una instancia de un nuevo servicio en cada solicitud puede ser costoso y le aconsejo, si puede, crear una instancia de todos ellos al principio y mantenerlos listos para las próximas solicitudes. Responderás más rápido (depende del servicio) y sabrás antes los recursos que necesita la app.
La respuesta es simple: no hay respuesta.
Bromas aparte, todo depende del caso de uso. Si tiene un servidor completo dedicado a su aplicación y ninguna otra aplicación en ejecución, entonces puede usar todos los recursos disponibles (no es que tenga que hacerlo) y, por lo tanto, no está sujeto a restricciones estrictas. Por otro lado, es posible que se esté ejecutando con recursos limitados, como un servidor compartido (como un servidor en la nube), donde se pueden ejecutar otros servicios y cambiar de un servidor a otro puede significar una pérdida de información.
No estoy seguro de lo que quiere decir con "servicio", pero si es solo una clase que se puede compartir entre varias solicitudes, entonces mi consejo es instanciarlos en un módulo (o paquete) y reutilizarlos entre las solicitudes.
Por supuesto, si requieren muchos recursos, digamos 0,5 GB de RAM, mientras que su servidor solo tiene 1 GB de RAM y los usuarios solicitan diferentes servicios, tan pronto como genere un segundo servicio, el sistema fallará.
En mi experiencia, Python (y especialmente las últimas versiones) son lo suficientemente eficientes para casi todos los casos de uso. La belleza es que puede escalarlo, pero nuevamente, todo depende del uso esperado, el contexto (qué servicios son) y las restricciones.
Ejemplo
Recientemente, he estado usando la biblioteca httpx para comunicarme con otro servicio en mi aplicación web. Estaba instanciando un cliente (o sesión para aquellos que provienen de requests
) para cada solicitud. En cierto momento comencé a recibir algunos errores, debido a un aumento de tráfico.
El problema era la creación de instancias en cada solicitud y tener miles de ellas, significaba realizar una conexión con el servicio continuamente. Entonces, instalé un cliente que se conectó una vez al iniciar el servidor y se compartió entre las diferentes funciones/rutas. Fue aún más rápido porque no tuve que conectarme en cada solicitud, sino solo una vez.