【问题标题】:How to configure Pyramid + uWSGI + SQLAlchemy如何配置 Pyramid + uWSGI + SQLAlchemy
【发布时间】:2017-01-26 12:48:02
【问题描述】:

我正在使用 Python 3.5、Pyramid 1.7、uWSGI 2.0.11 和 SQLAlchemy 1.0.9。我听说当使用 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 中使用预分叉的正确方法?我有点困惑,因为我真的不明白是否在引擎创建期间或调用engine.connect()时定义了与某个池的连接@

【问题讨论】:

    标签: python-3.x sqlalchemy pyramid uwsgi


    【解决方案1】:

    必须在分叉后做一件事,而且只能做一件事——在引擎上调用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/)。

    【讨论】:

      【解决方案2】:

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

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

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

      如果您使用 UWSGI 预分叉而不是读取,则可以使用 scoped_sessionscopefunc 参数使其为每个工作人员返回不同的连接 - 我想您可以使用 uwsgi.worker_id() 作为散列键。

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

      【讨论】:

      • 嗨@Sergey,感谢您的回复:) uWSGI 默认使用Preforking,它基本上只加载应用程序一次并与其他工作人员共享结果,这就是您可能会遇到问题共享的原因与 SQLAlchemy 或 Cassandra 的连接。 (只是添加了参考)。这就是为什么人们通常使用 lazy-apps 选项来配置 uWSGI 以避免这种行为。据我所知,像您建议的那样使用 SQL alchemy ORM 比核心实现要慢得多:)
      • @Ander:我在答案中添加了一段关于指定自定义范围的段落,这应该可以解决您的预分叉问题。我从不建议使用 ORM(它实际上并没有那么慢),我建议使用正确配置的 Session,它是连接/事务的抽象,而是从池中签出连接在每个请求上重新连接。如果您认为您自己的自行车改造方法比使用 Session 更快,那么您会大吃一惊:)
      • mmh.. 如果我错了请纠正我,但我相信会话只能在 ORM SQLAlchemy 中使用(它们是从 sqlalchemy.orm 导入的)并且没有会话SQLAlchemy 核心文档。我的应用程序目前太大了,无法从 Core 迁移到 ORM 实现。我的本土方法只是我在之前的aiopyramid project 中使用的方法,以避免 SQLAlchemy 出现异步问题。我不认为这是最好的,但到目前为止效果出奇的好:)
      • 有一个 Session.execute 方法“在当前事务中执行 SQL 表达式构造或字符串语句” - docs.sqlalchemy.org/en/latest/orm/… - 此方法的工作方式类似于 Connection.execute()
      • 这将简化很多交易,谢谢伙计。
      猜你喜欢
      • 2015-08-18
      • 1970-01-01
      • 1970-01-01
      • 2012-06-08
      • 1970-01-01
      • 2020-01-23
      • 2011-10-31
      • 2011-11-26
      • 2017-07-19
      相关资源
      最近更新 更多