我可以依靠垃圾收集器来关闭Python中的异步数据库连接吗?

问题描述 投票:0回答:1

我的团队正在开发一个用 Python 实现的异步 HTTP Web 服务器。我们使用 Redis 进行数据存储,并在 redis-py 库的帮助下连接到它。由于 HTTP 服务器是异步的,因此我们使用

redis.asyncio.Redis
客户端类 - 它在内部创建连接池并自动管理它。

Redis 服务器托管在 AWS 中,并将配置密码轮换。目前,我们正在尝试想出一种在 Python 代码中自动处理这一事实的方法。我们必须执行 2 个步骤:

  1. 一旦我们知道新的凭据可用,就创建一个新的连接池
  2. 一旦我们知道它将不再使用
  3. ,就关闭现有的连接池

这里的问题是步骤#2。不能保证我们能够引入任何同步机制来告诉我们连接池是否可以手动安全关闭(即此时没有依赖旧连接池的 HTTP 请求正在处理),所以我们首先寻找替代的自动化解决方案。现在我想知道我们是否可以依靠垃圾收集器来安全地关闭任何现有的连接。

根据 文档,必须手动关闭

redis.asyncio.Redis
实例,因为
__del__
魔术方法本质上是同步的,无法执行
await self.aclose()
本身。同时,我想知道如果这些对象只是被 GC 销毁会发生什么。理论上,清理过程应该是这样的:

  1. GC 销毁一个
    redis.asyncio.Redis
    实例(又名客户端)及其所有字段
  2. GC 销毁存储在该客户端内的连接池类实例
  3. GC 销毁存储在该连接池中的连接列表
  4. GC 销毁该列表中存储的所有连接类实例

我进行了与此类似的人工测试:

client = Redis(...)

await asyncio.gather(*(
    client.get(f"key_{i}") for i in range(100)
))

# checkpoint 1
client = Redis(...)
# checkpoint 2

Redis 服务器报告在检查点 #1 处打开了 100 个连接,在检查点 #2 处打开了 0 个连接(在一种情况下立即打开,在另一种情况略有不同的情况下,我必须使用新客户端向 Redis 服务器发出另一个请求)事先举例)。看起来(ab-)像这样使用 GC 不会让 Redis 服务器上挂起任何连接,但是我们能否确保 HTTP 服务器上的所有内容都会被正确清理,并且我们最终不会得到任何连接内存泄漏或挂起系统资源?

python asynchronous redis garbage-collection connection-pooling
1个回答
0
投票

简短回答:“我可以信赖……吗?”

是的。


您一直使用的两个术语我们应该仔细定义。

当你说“python”时,我选择解释它 作为“cPython 3.12 解释器” (或者几乎任何现代的 3.x 解释器)。

当你说“GC”时,我主要认为这是“古老的变量超出了范围”。

“python 语言”包含多种实现, 包括 Jython 和 Iron Python。 每个实施都有自己的管理方法 并回收内存分配。

cPython 字节码解释器当然有垃圾收集器。 但它很少执行,对于交易的特殊情况 具有循环数据结构。通常 引用计数 是我们感兴趣的。当对象的引用计数变为零时, 例如当它超出范围时, cPython 解释器立即回收其存储空间。 这是完全可预测的同步行为。 很少有 Python 应用程序严重依赖

__del__
方法 回收资源,因为
__del__
执行可能会 被推迟很长一段时间甚至无限期。

在java中我们常见到

x = null
, 告诉 GC 不再需要
x
现在是收集其存储的开放季节。 在Python中我们可以
del x
, 但这很少有用。 如果调用者仍然持有参考信息, 然后减少 our 引用计数 不会将其驱动到零,所以什么也不会发生。

显式

del
的情况往往是 最有用的是
del mydict[some_key]
, 以防止字典的存储 成长无极限。

© www.soinside.com 2019 - 2024. All rights reserved.