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

Jos*_*Fox 6 python multithreading flask

Flask 中的全局变量不是线程安全的或“进程安全的”

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

dav*_*ism 4

您链接的问题是在谈论数据,而不是连接。让多个工作人员改变全局数据并不好,因为您无法推断这些工作人员在 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)
Run Code Online (Sandbox Code Playgroud)

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