如何配置Pyramid + uWSGI + SQLAlchemy

时间:2016-09-18 21:26:40

标签: python-3.x sqlalchemy pyramid uwsgi

我正在开发一个使用Python 3.5 ,Pyramid 1.7 ,uWSGI 2.0.11 和SQLAlchemy 1.0.9 <的Web应用程序/ strong>即可。我听说当使用uWSGI和多个worker时,我们应该使用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中使用preforking的正确方法?我有点困惑,因为我真的不明白在创建引擎或调用engine.connect()时是否定义了与某个池的连接

2 个答案:

答案 0 :(得分:2)

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

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

  

当程序使用多处理或fork(),并且将Engine对象复制到子进程时,应调用Engine.dispose(),以便引擎在该fork的本地创建全新的数据库连接。数据库连接通常不会跨越进程边界。

从内存中,我的代码看起来像这样:

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

try:
    import uwsgi

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

except ImportError:
    pass

scoped_session内容涉及关闭/释放池中的连接 - 如果没有dispose,则会冒多个工作人员独立管理相同数据库连接的风险。

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

答案 1 :(得分:0)

“我听说当使用uWSGI和多个worker时,我们应该使用uWSGI postfork函数连接到SQLAlchemy数据库。否则SQLAlchemy将在不同的forks之间共享连接池,从而导致问题。” [引证需要] :)

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

SQLAlchemy的Session对象,当配置为scoped_session时,是一个线程本地的,所以虽然看起来你在线程之间共享一个全局变量,但该变量实际上代表了每个线程中的一个单独的连接/事务。

如果你使用UWSGI preforking而不是thereads,你可以使用scoped_sessionscopefunc参数使其返回每个worker的不同连接 - 我想你可以使用uwsgi.worker_id()作为哈希键。

我也不太明白你试图用def get_db()实现什么,但它看起来非常可疑 - 看起来你正在打开和关闭每个请求的数据库的新连接,是ewww ... :)我建议你看看stock Pyramid scaffolds中的一个,它说明了如何使用Pyramid配置SQLAlchemy。神奇的词语是“ZopeTransactionExtension”和“scoped_session”,如图所示here