Manera segura de la concurrencia para inicializar conexiones de datos globales en Flask

Las variables globales no son seguras para subprocesos porque una aplicación Flask puede generarse en múltiples procesos, no solo en múltiples subprocesos.

Sin embargo, necesito abrir conexiones a los servicios que cada trabajador utilizará, como un cliente PubSub o un cliente de almacenamiento en la nube. Parece que estos aún deben ser globales para que cualquier función de la aplicación pueda acceder a ellos. Para inicializarlos de forma perezosa, verifico si la variable es None , y esto debe ser seguro para subprocesos. ¿Cuál es el enfoque recomendado para abrir conexiones que utilizará cada solicitud? ¿Debo usar un locking de hilo para sincronizar?

La pregunta que has vinculado está hablando de datos , no de conexiones. Tener múltiples trabajadores mutando los datos globales no es bueno porque no puede razonar acerca de dónde están esos trabajadores en una aplicación web para mantenerlos sincronizados.

La solución a esa pregunta es usar una fuente de datos externa, como una base de datos, que debe estar conectada de alguna manera. Sin embargo, su idea de tener una conexión global no es segura, ya que varios subprocesos de trabajo interactuarían con ella simultáneamente y se mezclarían con el estado de cada uno o esperarían uno por uno para adquirir el recurso. La forma más sencilla de manejar esto es establecer una conexión en cada vista cuando la necesite.


Este ejemplo muestra cómo tener una conexión única por solicitud, sin globales, reutilizando la conexión una vez que se establece para la solicitud. El objeto g , si bien parece un objeto global, se implementa como un subproceso local tras bambalinas, por lo que cada trabajador obtiene su propia instancia g y la conexión almacenada en él solo durante una solicitud.

 from flask import g def get_conn(): """Use this function to establish or get the already established connection during a request. The connection is closed at the end of the request. This avoids having a global connection by storing the connection on the g object per request. """ if "conn" not in g: g.conn = make_connection(...) return g.conn @app.teardown_request def close_conn(e): """Automatically close the connection after the request if it was opened. """ conn = g.pop("conn", None) if conn is not None: conn.close() @app.route("/get_data") def get_data(): # If something else has already used get_conn during the # request, this will return the same connection. Anything # that uses it after this will also use the same connection. conn = get_conn() data = conn.query(...) return jsonify(data) 

Eventualmente, puede encontrar que establecer una nueva conexión cada solicitud es demasiado costoso una vez que tenga muchos miles de solicitudes concurrentes. Una solución es crear un grupo de conexiones para almacenar una lista de conexiones globalmente, con una forma segura de subprocesos para adquirir y reemplazar una conexión en la lista según sea necesario. SQLAlchemy (y Flask-SQLAlchemy) utiliza esta técnica. Muchas bibliotecas ya proporcionan implementaciones de grupos de conexión, por lo tanto, utilícelas o utilícelas como referencia para usted.