Безопасный для параллелизма способ инициализации глобальных подключений к данным во Flask - PullRequest
0 голосов
/ 16 января 2019

Глобальные переменные не поточнобезопасны , потому что приложение Flask может появляться в нескольких процессах, а не только в нескольких потоках.

Однако мне нужно открыть соединения со службами, которые будет использовать каждый работник, такими как клиент PubSub или клиент Cloud Storage. Кажется, что они все еще должны быть глобальными, чтобы любая функция в приложении могла получить к ним доступ. Чтобы лениво инициализировать их, я проверяю, является ли переменная None, и это должно быть поточно-ориентированным. Каков рекомендуемый подход для открытия соединений, который будет использовать каждый запрос? Должен ли я использовать блокировку потока для синхронизации?

1 Ответ

0 голосов
/ 16 января 2019

Вопрос, который вы связали , касается данных , а не соединений. Многопользовательская работа с глобальными данными нецелесообразна, потому что вы не можете понять, где эти сотрудники находятся в веб-приложении, чтобы синхронизировать их.

Решением этого вопроса является использование внешнего источника данных, такого как база данных, к которой необходимо каким-либо образом подключиться. Однако ваша идея иметь одно глобальное соединение небезопасна, поскольку несколько рабочих потоков будут взаимодействовать с ним одновременно и либо связываться друг с другом, либо ждать по одному для получения ресурса. Самый простой способ справиться с этим - установить соединение в каждом представлении, когда вам это нужно.


В этом примере показано, как создать уникальное соединение для каждого запроса, без глобальных переменных, повторно использовать соединение после его установления для запроса. Объект 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-SQLAlchemy) использует эту технику. Многие библиотеки уже предоставляют реализации пула соединений, поэтому либо используйте их, либо используйте в качестве справочной информации для себя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...