我正在开发一个使用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()
时是否定义了与某个池的连接
答案 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_session的scopefunc
参数使其返回每个worker的不同连接 - 我想你可以使用uwsgi.worker_id()
作为哈希键。
我也不太明白你试图用def get_db()
实现什么,但它看起来非常可疑 - 看起来你正在打开和关闭每个请求的数据库的新连接,是ewww ... :)我建议你看看stock Pyramid scaffolds中的一个,它说明了如何使用Pyramid配置SQLAlchemy。神奇的词语是“ZopeTransactionExtension”和“scoped_session”,如图所示here