将Twisted`enterprise.adbapi`中的查询添加到`twistd`守护进程创建的reactor循环中

时间:2011-10-24 23:37:06

标签: python twisted

我在Twisted twisted.enterprise.adbapi插件中使用.tac,并且发现除非调用aConnectionPool.runQuery(sqlQuery),否则为reactor.(run)等函数返回的延迟对象不会触发。如何将查询添加到twistd创建的reactor循环而不是调用reactor.run()?它是一般程序还是特定于异步数据库API的东西?

编辑 - 附上代码:

from twisted.application import internet, service
from zope.interface import implements
from twisted.web.iweb import IBodyProducer

from twisted.internet import defer, protocol, reactor
from twisted.internet.defer import succeed
from twisted.web.client import Agent
from twisted.web.http_headers import Headers

import json
import base64
from twisted.enterprise import adbapi

class StringProducer(object):
    implements(IBodyProducer)

    def __init__(self, body):
        self.body = body
        self.length = len(body)

    def startProducing(self, consumer):
        consumer.write(self.body)
        return succeed(None)

    def pauseProducing(self):
        pass

    def stopProducing(self):
        pass

def httpRequest(url, values, headers={}, method='POST'):

    agent = Agent(reactor)
    d = agent.request(method,
                      url,
                      Headers(headers),
                      StringProducer(values)
                      )

    def handle_response(response):
        if response.code == 204:
            d = defer.succeed('')
        else:
            class SimpleReceiver(protocol.Protocol):
                def __init__(s, d):
                    s.buf = ''; s.d = d
                def dataReceived(s, data):
                    s.buf += data
                    response = json.loads(data)

                    receipt = response[u'receipt']
                    if receipt[u'product_id'] == "com.domain_name.app_name.a_product_id":
                        transactionID = receipt[u'original_transaction_id']
                        date = receipt[u'original_purchase_date']
                        purchaseDate = date.strip(' Etc/GMT')
                        print transactionID
                        print purchaseDate

                        dbpool = adbapi.ConnectionPool('MySQLdb', db='mydb', user='user',  passwd='passwd')
                        dOperation = dbpool.runOperation("insert into users(name, original_transaction_id, date_joined) values(%s, %s, %s)", ('testuser', transactionID, purchaseDate))

                        def finishInsert(dObject, pool):
                            print 'inserted!'
                            pool.close()
                        dOperation.addCallback(finishInsert, dbpool)

                        def insertError(dObject):
                            print 'insert error!'
                        dOperation.addErrback(insertError)

                def connectionLost(s, reason):
                    s.d.callback(s.buf)

            d = defer.Deferred()
            response.deliverBody(SimpleReceiver(d))
        return d

    d.addCallback(handle_response)

class StoreServer(protocol.Protocol):

    def dataReceived(self, data):
        a = data.split(':delimiter:')

        if a[0] == 'addToUserList':
            receiptBase64 = base64.standard_b64encode(a[1])
            jsonReceipt = json.dumps({'receipt-data':receiptBase64})

            httpRequest(
                        "https://buy.itunes.apple.com/verifyReceipt",
                        jsonReceipt,
                        {'Content-Type': ['application/x-www-form-urlencoded']}
                        )

application = service.Application("My Server")
storeFactory = protocol.Factory()
storeFactory.protocol = StoreServer
tcpStoreServer = internet.TCPServer(30000, storeFactory)
tcpStoreServer.setServiceParent(application)

1 个答案:

答案 0 :(得分:2)

您的代码会为其发出的每个请求生成一个新的ConnectionPool。新的ConnectionPool创建自己的新线程池以运行查询,并且必须建立与数据库的新连接。

这意味着您实际上没有连接池。您只需创建并使用一次连接即可。此外,errback insertError不会关闭池。

这些结合在一起意味着可以一次创建的线程/连接数没有限制,除了系统对可以分配的内存量或者可以打开多少个套接字所施加的限制。当你遇到其中一个限制时,事情就不会很好了。

这也意味着每个查询错误都会泄漏一些线程和连接(ConnectionPool在启动时设置3个线程/连接)。在出现足够的错误后,您将无法再创建任何线程或连接,因此您将无法再查询数据库。您的查询很简单,您可能认为错误的可能性不大,但MySQL往往会随意断开客户端(也许您至少有点意识到这一点,因为您 添加了errback报告失败)。

ConnectionPool的预期用途是创建一个(或两个,或其他一些小的固定数字),然后将其重新用于所有查询。这些问题是否与您最初观察到的问题有关,我不知道,但它们可能是您应该解决的问题。