sqlalchemy scoped_session

SQLAlchemy 的 scoped_session 提供了一种管理会话范围的方式,尤其适用于 web 应用程序。它使用线程局部存储,确保每个线程都有其独立的会话实例,从而在不同线程间隔离会话。scoped_session 可以被当作会话对象来使用,隐式地代理会话的方法。在 web 请求结束时,通常需要调用 scoped_session.remove() 来清理资源。对于多线程 web 应用,使用 scoped_session 可以简化会话管理,但最好还是利用 web 框架的集成工具来实现更精确的请求关联。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Contextual/Thread-local Sessions

上下文、线程本地会话

回想一下我们在什么时候创建一个会话, 什么时候提交它, 什么时候关闭它? 引入了“”“会话范围” 的概念, 重点是web 应用程序将会和web请求相链接的实践。 大多数现代web 框架都包含集成工具, 因此可以自动管理会话的范围, 并且在可用的时候使用这些工具。

sqlalchemy 包含自己的助手对象, 这有助于建立用户自定义的会话范围。他还被第三方集成系统用于帮助构建他们的集成方案。

对象是scoedsession 对象, 它代表的会话对象的注册表。 如果你对注册别的模式不熟悉, 那么可以在企业架构的模式里面找到一个很好的解释。

注意:

scoped_session 对象是许多sqlalchemy 应用程序使用的非常流行和有用的对象。 然而, 中药的是要注意, 它只是提供了一种处理会话管理问题的方法。 如果你是sqlalchemy的新手, 特别是如果术语 “”“线程局部变量”, 在你看起来很奇怪, 那么我们建议你首先熟悉一个现成的集成框架系统, 例如 Flash-Sqlalchemy 或者zope.sqlalchemy 

一个scopedsession 是通过调用它来构造的, 它传递一个可以创建新的会话对象的工厂。工厂只是在被调用时产生新对象的东西, 在会话的情况下, 最长见的工厂是在北街前面介绍的sessionmaker.,下面来说明这个用法:

>>> from sqlalchemy.orm import scoped_session
>>> from sqlalchemy.orm import sessionmaker

>>> session_factory = sessionmaker(bind=some_engine)
>>> Session = scoped_session(session_factory)

我们创建的scoped_session 对象将在我们 “”“调用” 登记处时候调用sessionmaker

>>> some_session = Session()

在上面, 有些会话是会话的实例, 我们现在可以使用他来与数据库对话。 同样的会话也会存在于我们创建的scoped_session 注册表中, 如果我们第二次访问注册表, 我们会回到同一个会话;

>>> some_other_session = Session()
>>> some_session is some_other_session
True

这种模式 允许应用程序的不同部分调用全局的scoped_session, 这样所有的这些区域可以共享同一个会话, 而不是显示的进行传递, 我们在注册表中建立的会话会保留, 直到我们明确的告诉注册中心处理它,通过调用scoped_session.remove()

>>> Session.remove()

sopped_session.remove() 方法会首先调用.close() 方法, 它的作用是释放会话所拥有的任何链接、事务资源的效果, 然后丢弃会话本身。“释放” 意味着链接呗返回到他们的连接池, 任何事务状态都会被回滚, 最终使用低层的ABAPI 链接rollback() 的方法。

在这一点上,scoped_session 对象是‘空的’, 并再次调用的时候创建一个新的会话, 如下图所示这和之前创建的session 是不同的。

>>> new_session = Session()
>>> new_session is some_session
False

上面一系列的步骤概括了 “注册表” 模式的概念。 有了这个基本的想法, 我们就可以讨论下这个注册米欧式的一些细节

隐式 方法调用

scopedsession 的工作很简单, 为所有的请求保留一个session。 作为一种对会话进行更透明的访问的方法, scoped_session 还包括代理行为, 这以为这注册本身就可以直接当做一个会话来处理。当在这个对象上调用方法适合, 他们被代理到由注册维护的低层会话。

Session = scoped_session(some_factory)

# equivalent to:
#
# session = Session()
# print(session.query(MyClass).all())
#
print(Session.query(MyClass).all())

上面的代码完成了与获取当前会话相同的任务,通过调用注册, 然后使用该会话

线程局部 范围(线程本地存储)

熟悉多线程编程的用户会注意到, 表示任何作为全局变量的东西通常都是一个坏注意, 因为它暗示了全局对象将被许多线程并发的访问。会话对象完全别设计成以非并发的方式使用, 在多线程方面, 她的意思是“一次只在一个线程中”。因此, 我们上面的scoped_session 用法的例子, 在多个调用中维护同一个session对象, 这表明需要一个过程, 这个多个线程之间的多次调用实际上并不能得到相同会话的句柄, 我们把这个概念称为线程本地存贮, 也就是说, 使用一个特殊的对象来维护每一个应用程序线程的不同对象。python 通过thread.local() 概念提供了这一点。 scoped_session 对象在默认的情况下使用这个对象作为存储器, 这样就可以为所有调用scoped_session 注册表的人维护一个会话, 但是只是在单线程的范围内, 在不同的线程中调用注册的调用者会得到一个本地到另外一个线程的会话实例。

通过使用这个技术, scoped_session 提供了一个快速且简单的方法(如果你熟悉线程的本地存储), 那么在应用程序中提供一个全局对象, 该应用程序可以安全的从多个线程调用。

scoped_session.remove() 方法 通常会删除与线程相关的当前会话(如果有的话)。然而thread.local()对象的一个优点是, 如果应用线程本身终止, 该线程的 “存储” 也会被垃圾收集。 因此, 使用线程局部变量作用域与生成和分解线程的应用程序一起使用线程是安全的。而不需要调用scoped_session.remove().但是, 事务本省的范围(即通过Session.commit()或者session.rollback()结束他们) 仍然是必须在适当的时间显示安排的事情, 除非应用程序实际上将线程的生命周期与事务的生命周期联系起来。

使用线程局部范围和web应用程序(Using Thread-Local Scope with Web Applications)

正如本节所讨论的, 我什么时候构建一个会话, 什么时候提交它, 什么时候关闭它?web 应用程序是围绕web请求的概念构造的, 而将此类应用程序与会话集成通常通常以为着会话将与该请求关联。事实证明, 除了异步框架Twister和Tornado 等明显异常外,大多数python框架都已简单的方式使用线程, 例如在单个工作线程范围内接受, 处理和完成特定的web 请求。当请求结束的时候, 工作线程被释放到一个工作线程池中, 在哪里她可以处理另一个请求

这个简单的web请求的信件和线程意味着将一个会话与线程意味着它也涉及web请求的线程中运行,反之亦然,如果会话创建web请求后才开始和拆除前web请求结束。因此,使用scoped_session作为将会话与web应用程序集成的快速方法是一种常见的实践。下面的序列图说明了这个流程:

Web Server          Web Framework        SQLAlchemy ORM Code
--------------      --------------       ------------------------------
startup        ->   Web framework        # Session registry is established
                    initializes          Session = scoped_session(sessionmaker())

incoming
web request    ->   web request     ->   # The registry is *optionally*
                    starts               # called upon explicitly to create
                                         # a Session local to the thread and/or request
                                         Session()

                                         # the Session registry can otherwise
                                         # be used at any time, creating the
                                         # request-local Session() if not present,
                                         # or returning the existing one
                                         Session.query(MyClass) # ...

                                         Session.add(some_object) # ...

                                         # if data was modified, commit the
                                         # transaction
                                         Session.commit()

                    web request ends  -> # the registry is instructed to
                                         # remove the Session
                                         Session.remove()

                    sends output      <-
outgoing web    <-
response

使用上述流程, 将会话与web应用程序集成的的过程有两个要求:

 1、当web应用程序启动的时候, 创建一个scoped_session注册表, 确保该对象被应用程序的其他部分访问。
 2、确保在web请求结束的时候调用scoped_session.remove(), 通常通过与web框架的事件系统集成来创建一个 “on request end 事件”

如前所述,上面的模式只是将会话与web框架集成的一种潜在方式,其中一种方式特别强调web框架将web请求与应用程序线程关联起来。然而,强烈建议使用web框架本身提供的集成工具(如果可用的话),而不是scoped_session。

特别是,虽然使用线程local可能很方便,但是最好将会话直接与请求关联,而不是与当前线程关联。关于自定义作用域的下一节详细介绍了一种更高级的配置,它可以将scoped_session的用法与基于直接请求的作用域或任何类型的作用域结合起来。

使用自定义范围创建

scoped_session对象的默认行为“thread local”作用域只是关于如何“作用域”会话的众多选项之一。自定义作用域可以基于任何现有系统来定义“我们正在使用的当前事物”。

假设web框架定义了一个库函数get_current_request()。使用此框架构建的应用程序可以在任何时候调用此函数,结果将是某种表示正在处理的当前请求的请求对象。如果请求对象是hashable,那么这个函数可以很容易地与scoped_session集成,以将会话与请求关联起来。下面我们将结合web框架on_request_end提供的假设事件标记来说明这一点,该标记允许在请求结束时调用代码:

from my_web_framework import get_current_request, on_request_end
from sqlalchemy.orm import scoped_session, sessionmaker

Session = scoped_session(sessionmaker(bind=some_engine), scopefunc=get_current_request)

@on_request_end
def remove_session(req):
    Session.remove()

在上面,我们以通常的方式实例化scoped_session,只是我们将请求返回函数传递为“scopefunc”。这指示scoped_session在调用注册表以返回当前会话时使用此函数生成一个dictionary键。在这种情况下,确保实现可靠的“删除”系统尤为重要,因为在其他情况下,字典不是自管理的。

参考文章

http://docs.sqlalchemy.org/en/latest/orm/contextual.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值