Twisted的关系数据库支持(Twisted RDBMS support)

-- Jerry Marx [DateTime(2004-09-15T04:29:50Z)] TableOfContents

概要(Abstract)

Twisted is an asynchronous networking framework, but most database API implementations unfortunately have blocking interfaces -- for this reason, twisted.enterprise.adbapi was created. It is a non-blocking interface to the standardized DB-API 2.0 API, which allows you to access a number of different RDBMSes.

Twisted是异步的网络编程框架,不幸的是大多数数据库API的实现都是阻塞的接口,因为这个原因创造了twisted.enterprise.adbapi.它是一个符合DB-API 2.0 标准的非阻塞接口,使用它可以访问很多关系数据库

你应该已经知道的(What you should already know)

快速回顾(Quick Overview)

Twisted is an asynchronous framework. This means standard database modules cannot be used directly, as they typically work something like:

Twisted是一个异步编程框架,这就意味着标准的数据库模块不能直接使用,他们的典型用法如下:

   1 # Create connection... 
   2 db = dbmodule.connect('mydb', 'andrew', 'password') 
   3 # ...which blocks for an unknown amount of time 
   4  
   5 # Create a cursor 
   6 cursor = db.cursor() 
   7  
   8 # Do a query... 
   9 resultset = cursor.query('SELECT * FROM table WHERE ...') 
  10 # ...which could take a long time, perhaps even minutes.

Those delays are unacceptable when using an asynchronous framework such as Twisted. For this reason, twisted provides twisted.enterprise.adbapi, an asynchronous wrapper for any DB-API 2.0-compliant module.

对于像Twisted这样的异步编程框架来说延迟是不能接受的.因为这个原因,twisted提供了twisted.enterprise.adbapi,它是一个对于任何兼容DB-API 2.0 的异步包装模块.

enterprise.adbapi will do blocking database operations in seperate threads, which trigger callbacks in the originating thread when they complete. In the meantime, the original thread can continue doing normal work, like servicing other requests.

enterprise.adbapi在独立的线程里面执行阻塞的数据库操作,而是在操作完成后触发原线程的回调函数.同时,原始线程可以继续执行,比方说处理其他的请求.

怎么使用adbapi(How do I use adbapi?)

Rather than creating a database connection directly, use the adbapi.ConnectionPool class to manage a connections for you. This allows enterprise.adbapi to use multiple connections, one per thread. This is easy:

使用adbapi.ConnectionPool类来管理你的数据库连接,而不要直接连接.这个类可以使用多个连接,比方说每个线程一个连接,非常容易:

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

Things to note about doing this:

这么做的时候要注意以下这些事情:

Now we can do a database query:

现在我们可以做数据库查询了:

   1 # equivalent of cursor.execute(statement), return cursor.fetchall():
   2 def getAge(user):
   3     return dbpool.runQuery("SELECT age FROM users WHERE name = ?", user)
   4 
   5 def printResult(l):
   6     if l:
   7         print l[0][0], "years old"
   8     else:
   9         print "No such user"
  10 
  11 getAge("joe").addCallback(printResult)

This is straightforward, except perhaps for the return value of getAge. It returns a twisted.internet.defer.Deferred, which allows arbitrary callbacks to be called upon completion (or upon failure). More documentation on Deferred is available here.

除了getAge的返回值可能有些奇怪以外,上面的代码一目了然.getAge返回了一个twisted.internet.defer.Deferred,可以给它添加任何的成功回调(或者错误回调).关于Deferred的更多信息可以查阅文档.

In addition to runQuery, there is also runOperation, and runInteraction that gets called with a callable (e.g. a function). The function will be called in the thread with a twisted.enterprise.adbapi.Transaction, which basically mimics a DB-API cursor. In all cases a database transaction will be commited after your database usage is finished, unless an exception is raised in which case it will be rolled back.

除了runQuery外,还有runOperation和runInteraction可以杯一个可调用实体(比方说一个函数)来调用.这个函数会在一个拥有twisted.enterprise.adbapi.Transaction的线程里调用,它简单模拟了一个DB-API游标.

def _getAge(txn, user):

def getAge(user):

def printResult(age):

getAge("joe").addCallback(printResult)

Also worth noting is that these examples assumes that dbmodule uses the qmarks paramstyle (see the DB-API specification). If your dbmodule uses a different paramstyle (e.g. pyformat) then use that. Twisted doesn't attempt to offer any sort of magic paramater munging -- runQuery(query, params, ...) maps directly onto cursor.execute(query, params, ...).

这样!(And that's it!)