在 SQLAlchemy 中正确使用与异步服务器的会话

Posted

技术标签:

【中文标题】在 SQLAlchemy 中正确使用与异步服务器的会话【英文标题】:Correct usage of session with asynchronous servers in SQLAlchemy 【发布时间】:2012-11-10 18:56:53 【问题描述】:

背景:

我们有一个使用 SqlAlchemy 作为 ORM 的 Python Web 应用程序。我们目前使用 Gunicorn(sync worker) 运行这个应用程序。此应用程序仅用于响应长时间运行的请求(即提供大文件,请不要建议使用 X-Sendfile/X-Accel-Redirect,因为响应是从 Python 应用程序动态生成的)。

使用 Gunicorn 同步工作器,当我们运行 8 个工作器时,仅同时处理 8 个请求。由于所有这些响应都是 IO 绑定的,因此我们希望切换到异步工作器类型以获得更好的吞吐量。

我们在 Gunicorn 配置文件中将 worker 类型从 sync 切换为 eventlet。现在我们可以同时响应所有请求,但又出现了另一个神秘(对我来说很神秘)的问题。

在应用程序中,我们在模块级别有一个作用域会话对象。以下代码来自我们的 orm.py 文件:

uri = 'mysql://%s:%s@%s/%s?charset=utf8&use_unicode=1' % (\
    config.MYSQL_USER,
    config.MYSQL_PASSWD,
    config.MYSQL_HOST,
    config.MYSQL_DB,
)

engine = create_engine(uri, echo=False)

session = scoped_session(sessionmaker(
    autocommit=False,
    autoflush=False,
    bind=engine,
    query_cls=CustomQuery,
    expire_on_commit=False    
))

我们的应用程序使用这样的会话:

from putio.models import session

f = session.query(File).first()
f.name = 'asdf'
session.add(f)
session.commit()

当我们使用同步工作者时,会话是一次从 1 个请求中使用的。在我们切换到异步 eventlet worker 之后,同一个 worker 中的所有请求都共享同一个 session,这是不需要的。当会话在一个请求中提交,或者发生异常时,所有其他请求都会失败,因为会话是共享的。

在 SQLAlchemy 的文档中,说 scoped_session 用于线程环境中的单独会话。异步工作者中的 AFAIK 请求在同一线程中运行。

问题:

我们希望异步工作者中的每个请求都有单独的会话。在 SQLAlchemy 中使用异步工作者会话的正确方法是什么?

【问题讨论】:

【参考方案1】:

使用scoped_session's scopefunc argument。

【讨论】:

以上是关于在 SQLAlchemy 中正确使用与异步服务器的会话的主要内容,如果未能解决你的问题,请参考以下文章

如何访问与异步 sqlalchemy 的关系?

异步 SQLAlchemy 无法创建引擎

对Tornado异步操作Sqlalchemy方法的选定 不错

异步 SQLalchemy:访问急切加载的空关系会触发新的延迟加载,引发错误

如何反转连接顺序以使用子查询与 sqlalchemy 正确连接?

SQLAlchemy的同步和异步的代码对比