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

x6492ojm  于 4个月前  发布在  Redis
关注(0)|答案(1)|浏览(62)

我的团队正在开发一个用Python实现的异步HTTP Web服务器(确切地说是CPython 3.11)。我们使用Redis进行数据存储,并在redis-py库的帮助下连接到它。由于HTTP服务器是异步的,我们使用redis.asyncio.Redis客户端类-它在内部创建一个连接池并自动管理它。
Redis服务器托管在AWS中,并且将配置密码旋转。目前,我们正在尝试在Python代码中自动处理这个事实的方法。我们必须执行2个步骤:
1.一旦我们知道有新的凭据可用,就创建一个新的连接池
1.一旦我们知道它将不再被使用,就关闭现有的连接池
这里的问题是第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服务器上的所有内容都被正确地清理了吗?我们不会以任何内存泄漏或挂起系统资源而告终吗?

new9mtju

new9mtju1#

简短的回答:“我可以信赖......吗?”
是的
有两个术语你一直在用我们应该仔细定义。
当你说“python”时,我选择将其解释为“cPython 3. 12解释器”(或者几乎任何现代的3. x解释器)。
你说的“GC”,我通常认为是“古代变量超出范围”。
“python语言”包含了几种实现,包括Jython和Iron Python。每种实现都有自己的方法来管理和回收内存分配。
cPython字节码解释器当然有一个垃圾收集器。但是它很少被执行,对于处理循环数据结构的特殊情况。通常reference counting是我们感兴趣的。当一个对象的ref计数为零时,例如当它超出作用域时,cPython解释器会立即回收它的存储空间。2这是一种完全可以预测的同步行为。3很少有Python应用程序严重依赖__del__方法来回收资源,因为__del__执行可能会被延迟很长时间,甚至是无限期地延迟。
在java中我们经常看到x = null,告诉GC x不再需要了,现在是收集它以前指向的存储的开放季节。在python中我们 * 可以 * 写del x,但这很少有用。如果调用者仍然持有一个引用,那么递减 * 我们的 * 引用计数不会将它驱动到零,所以什么都不会发生。
显式del最有用的情况是del mydict[some_key],它可以防止字典的存储空间无限增长。
你没有滥用任何GC设施。
只要异步例程最终return,那么是的,redis对象将被回收而没有内存泄漏。

相关问题