在Flask中初始化全局数据连接的并发安全方式



全局变量不是线程安全的或"过程安全";在弗拉斯克。

但是,我需要打开到每个工作人员将使用的服务的连接,例如PubSub客户端或云存储客户端。似乎这些仍然需要是全局的,以便应用程序中的任何函数都可以访问它们。为了延迟初始化它们,我检查变量是否为None,并且这需要是线程安全的。打开每个请求将使用的连接的建议方法是什么?我应该使用线程锁进行同步吗?

您链接的问题是关于数据,而不是连接。让多个工作人员更改全局数据是不好的,因为你无法推断这些工作人员在web应用程序中的位置以保持他们的同步。

这个问题的解决方案是使用外部数据源,比如数据库,它必须以某种方式连接。但是,拥有一个全局连接的想法是不安全的,因为多个工作线程会同时与之交互,要么扰乱彼此的状态,要么一次等待一个线程来获取资源。处理此问题的最简单方法是在需要时在每个视图中建立连接


这个例子展示了如何在没有全局变量的情况下为每个请求建立一个唯一的连接,并在为请求建立连接后重用该连接。g对象虽然看起来像一个全局对象,但在后台被实现为一个本地线程,因此每个工作线程只在一个请求期间获得自己的g实例和存储在其上的连接。

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)

一旦您有成千上万的并发请求,您可能最终会发现每个请求建立一个新连接的成本太高。一种解决方案是构建一个连接池来全局存储连接列表,并使用线程安全的方式根据需要获取和替换列表中的连接。SQLAlchemy(和Flask SQLAlchemey)使用了这种技术。许多库已经提供了连接池实现,所以要么使用它们,要么将它们用作自己的参考。

最新更新