twisted.enterprise.adbapi:Twisted RDBMS 支持

摘要

Twisted 是一个异步网络框架,但大多数数据库 API 实现不幸地具有阻塞接口 - 因此,twisted.enterprise.adbapi 应运而生。它是一个针对标准化 DB-API 2.0 API 的非阻塞接口,允许您访问许多不同的 RDBMS。

您应该已经了解的内容

  • Python :-)

  • 如何编写一个简单的 Twisted 服务器(请参阅 本教程 了解如何编写)

  • 熟悉使用数据库接口(请参阅 DBAPI 2.0 文档

快速概述

Twisted 是一个异步框架。这意味着无法直接使用标准数据库模块,因为它们通常的工作方式类似于

# Create connection...
db = dbmodule.connect('mydb', 'andrew', 'password')
# ...which blocks for an unknown amount of time

# Create a cursor
cursor = db.cursor()

# Do a query...
resultset = cursor.query('SELECT * FROM table WHERE ...')
# ...which could take a long time, perhaps even minutes.

当使用像 Twisted 这样的异步框架时,这些延迟是不可接受的。为此,Twisted 提供了 twisted.enterprise.adbapi,它是任何 DB-API 2.0 兼容模块的异步包装器。

adbapi 将在单独的线程中执行阻塞数据库操作,这些操作在完成时会在源线程中触发回调。在此期间,源线程可以继续执行正常工作,例如服务其他请求。

如何使用 adbapi?

不要直接创建数据库连接,而是使用 adbapi.ConnectionPool 类来为您管理连接。这允许 adbapi 使用多个连接,每个线程一个。这很简单

# Using the "dbmodule" from the previous example, create a ConnectionPool
from twisted.enterprise import adbapi
dbpool = adbapi.ConnectionPool("dbmodule", 'mydb', 'andrew', 'password')

关于执行此操作需要注意的事项

  • 无需直接导入 dbmodule。您只需将名称传递给 adbapi.ConnectionPool 的构造函数。

  • 您传递给 dbmodule.connect 的参数作为额外的参数传递给 adbapi.ConnectionPool 的构造函数。关键字参数也适用。

现在我们可以执行数据库查询了

# equivalent of cursor.execute(statement), return cursor.fetchall():
def getAge(user):
    return dbpool.runQuery("SELECT age FROM users WHERE name = ?", user)

def printResult(l):
    if l:
        print(l[0][0], "years old")
    else:
        print("No such user")

getAge("joe").addCallback(printResult)

这很简单,除了 getAge 的返回值。它返回一个 Deferred,它允许在完成(或失败)时调用任意回调。有关 Deferred 的更多文档,请参阅 此处

除了 runQuery 之外,还有 runOperationrunInteraction,它们会使用可调用对象(例如函数)进行调用。该函数将在具有 adbapi.Transaction 的线程中调用,该线程基本上模拟了 DB-API 游标。在所有情况下,数据库事务将在您的数据库使用完成后提交,除非引发异常,在这种情况下,它将回滚。

def _getAge(txn, user):
    # this will run in a thread, we can use blocking calls
    txn.execute("SELECT * FROM foo")
    # ... other cursor commands called on txn ...
    txn.execute("SELECT age FROM users WHERE name = ?", user)
    result = txn.fetchall()
    if result:
        return result[0][0]
    else:
        return None

def getAge(user):
    return dbpool.runInteraction(_getAge, user)

def printResult(age):
    if age != None:
        print(age, "years old")
    else:
        print("No such user")

getAge("joe").addCallback(printResult)

还需要注意的是,这些示例假设 dbmodule 使用“qmarks”paramstyle(请参阅 DB-API 规范)。如果您的 dbmodule 使用不同的 paramstyle(例如 pyformat),则使用它。Twisted 不会尝试提供任何神奇的参数整理 - runQuery(query, params, ...) 直接映射到 cursor.execute(query, params, ...)

各种数据库适配器的示例

请注意,第一个参数是您通常导入并从中获取 connect(...) 的模块名称,后面的参数是您调用 connect(...) 时使用的任何参数。

from twisted.enterprise import adbapi

# PostgreSQL PyPgSQL
cp = adbapi.ConnectionPool("pyPgSQL.PgSQL", database="test")

# MySQL
cp = adbapi.ConnectionPool("MySQLdb", db="test")

就是这样!

这就是您在 Twisted 中使用数据库所需了解的全部内容。您可能应该阅读 adbapi 模块的文档,以了解它具有的其他函数,但希望本文档能介绍核心思想。