如何配置 Pyramid + uWSGI + SQLAlchemy

How to configure Pyramid + uWSGI + SQLAlchemy

我正在使用 Python 3.5、Pyramid 1.7、uWSGI 2.0 开发网络应用程序。 11 和 SQLAlchemy 1.0.9。我听说当与多个工作人员一起使用 uWSGI 时,我们应该使用 uWSGI postfork 函数来连接到 SQLAlchemy 数据库。否则 SQLAlchemy 将在导致问题的不同分支之间共享连接池:

根据这个建议,我在文件 my_app/__ini__.py 的金字塔应用程序中添加了这段代码,用于在 postfork 事件之后创建连接引擎:

def main(global_config, **settings):

    try:
        from uwsgidecorators import postfork
    except ImportError:
        # We're not in a uWSGI context, no need to hook dbs connection
        # to the postfork event.
        engine = engine_from_config(settings, prefix='sqlalchemy.')

    else:
        @postfork
        def init():
            """ Initialize dbs connexions in the context.
                Ensures that a new connexion is returned for every new request.
            """
            global engine
            engine = engine_from_config(settings, prefix='sqlalchemy.')


    # Retrieves database connection
    def get_db(request):
        global engine
        connection = engine.connect()
        def disconnect(request):
            connection.close()
        request.add_finished_callback(disconnect)
        return connection

    config = Configurator(settings=settings, root_factory=my_factory)
    config.add_request_method(get_db, 'db', reify=True)
    config.scan()
    return config.make_wsgi_app() 

有经验的人可以确认这是否是在 uWSGI 中使用预分叉的正确方法吗?我有点困惑,因为我不太明白是否在引擎创建期间或调用 engine.connect()

时定义了与某个池的连接

"I have heard that when using uWSGI with multiple workers we should use a uWSGI postfork function to connect to the SQLAlchemy database. Otherwise SQLAlchemy will share the connection pool between the different forks causing issues." [需要引用] :)

根据我的经验,标准 SQLAlchemy 设置与 UWSGI 的多进程或多线程模型没有任何问题,应用程序根本不需要知道 UWSGI。

SQLAlchemy 的 Session 对象,当配置为 scoped_session 时,是线程本地的,因此虽然看起来您在线程之间共享一个全局变量,但该变量实际上代表一个个体 connection/transaction 在每个线程中。

如果您使用 UWSGI preforking 而不是 thereads,您可以使用 scoped_sessionscopefunc 参数来使它 return 每个工作人员不同的连接 - 我想你可以使用 uwsgi.worker_id() 作为散列键。

我也不太明白你想用 def get_db() 实现什么,但它看起来非常可疑 - 看起来你在每次请求时都打开和关闭与数据库的新连接, 这是 ewww... :) 我建议你看一看 stock Pyramid scaffolds which illustrate how to configure SQLAlchemy with Pyramid. The magic words are "ZopeTransactionExtension" and "scoped_session" as illustrated here

必须在分叉后做一件事——调用引擎上的dispose()方法。

根据 sqlalchemy 的文档:http://docs.sqlalchemy.org/en/latest/core/connections.html#engine-disposal

When a program uses multiprocessing or fork(), and an Engine object is copied to the child process, Engine.dispose() should be called so that the engine creates brand new database connections local to that fork. Database connections generally do not travel across process boundaries.

根据记忆,我的代码看起来是这样的:

engine = engine_from_config(settings, prefix='sqlalchemy.')

try:
    import uwsgi

    def postfork():
        engine.dispose()
    uwsgi.post_fork_hook = postfork

except ImportError:
    pass

scoped_session 处理池中的 closing/releasing 个连接——没有 dispose,您 运行 多个工作人员独立管理同一个数据库连接的风险.

tldr; fork-safe 和 thread-safe 在 python 中是不同的概念。 (另见 http://www.dctrwatson.com/2010/09/python-thread-safe-does-not-mean-fork-safe/)。