共享txpostgres连接池

时间:2014-10-02 22:24:02

标签: python postgresql twisted psycopg2

我们有一个RESTful(-ish)扭曲的应用程序,它使用txpostgres来访问postgres数据库。目前,每次客户端ping服务器进行db调用时,我们都会生成新的txpostgres.Connection实例。这是低效的,导致我们的数据库很快就会不堪重负。我一直试图改编它以使用txpostgres.ConnectionPool代替,但遇到了麻烦。现在我有一些看起来像这样的东西:

class DBTester(object):
    def __init__(self):
        self.cfg = load_config('local')  # load the db settings from a JSON file
        self.pool = ConnectionPool(None, min=1, **self.cfg) # create the pool

    @defer.inlineCallbacks
    def get_pool(self):
        yield self.pool.start()
        defer.returnValue(self.pool)


class DBT(object):
    def __init__(self):
        self.db = DBTester()

    @defer.inlineCallbacks
    def t(self):
        conn = yield self.db.get_pool()
        res = yield conn.runQuery('select * from clients')
        println('DBT.t result: {}'.format(res))


if __name__ == "__main__":
    dbt = DBT()
    dbt.t()
    dbt.t()

    reactor.run()

问题是pool.start()电话的时间安排。如果我将其放入DBTester.__init__,我会获得psycopg2.OperationalError: asynchronous connection attempt underway。如果我将其放入DBTester.get_pool,则一个db.t()调用有效,另一个调用失败exceptions.AttributeError: 'NoneType' object has no attribute 'runQuery'。我基本上整天都在努力解决这个问题,而且还没有能够破解它,也没能在网上找到太多。

我真的只需要一个指向如何使用ConnectionPool的最小例子的指针。有什么建议吗?

2 个答案:

答案 0 :(得分:4)

听起来你的问题不在于txpostgres,而在于扭曲和异步的思维方式。

exceptions.AttributeError:' NoneType'对象没有属性' runQuery'的意思是: 您尝试在建立连接之前在数据库之后抛出SQL查询。那太蠢了!所以现在我想我会抛出一个例外,所以亲爱的用户知道这种疯狂。

所以,如果你有像

那样的话,就会发生这种情况
pool = ConnectionPool(None, min=1)
d1 = pool.start()
d2 = pool.runQuery('select tablename from pg_tables')

此代码在reactor中创建两个deferred和thorws em。只有调度算法知道首先执行的两个中的哪一个,如果它是d2,那么就会发生错误。

txpostgres.txpostgres.AlreadyConnected 表示: 非常自我解释,启动已经启动的池是没有意义的。

psycopg2.OperationalError:正在进行的异步连接尝试意味着:
当您开始执行SQL语句时,我正在设置一个漂亮的异步数据库连接。数据库连接还没有准备好,因此没有执行sql查询这让我很难过。我想我会抛出一个操作错误,所以亲爱的用户知道该声明失败了。

好的,所以我们需要一种方法来确保在我们在数据库之后抛出sql查询之前建立连接。下面是一个使用回调来实现此目的的代码示例。

from txpostgres.txpostgres import ConnectionPool
from twisted.internet import reactor, defer
from twisted.python import log, util


class SomeClass(object):

    pool = ConnectionPool(
        None,
        min=1,
        user="user",
        password="pass",
        host='host.com')

    @defer.inlineCallbacks
    def fetch_tables(self):
        res = yield self.pool.runQuery('select tablename from pg_tables')
        defer.returnValue(res)


if __name__ == "__main__":

    def querydb(n=10):
        dl = []
        for i in range(n):
            d = s.fetch_tables()
            d.addCallback(lambda tables: util.println(len(tables)))
            dl.append(d)
        return defer.DeferredList(dl)

    s = SomeClass()
    d_startpool = s.pool.start()
    d_startpool.addCallback(lambda _: querydb())
    d_startpool.addCallback(lambda _: s.pool.close())
    d_startpool.addErrback(log.err)
    d_startpool.addBoth(lambda _: reactor.stop())

    reactor.run()

希望这有帮助。

答案 1 :(得分:0)

我不知道这是否算作最佳做法,但这就是我们的目标:

## dbutil.py
class DBConnection(object):
    def __init__(self, cfg_name):
        self.cfg_name = cfg_name
        self.cfg = self.load_config(self.cfg_name)
        self.pool = txpostgres.ConnectionPool(None, min=5, **self.cfg)

    @staticmethod
    def load_config(name):
        with open('config.json') as json_file:
            cfg = json.load(json_file)
        return cfg.get(name)

    @defer.inlineCallbacks
    def get_pool(self):
        try:
            yield self.pool.start()
        except txpostgres.AlreadyConnected:
            pass
        defer.returnValue(self.pool)


@defer.inlineCallbacks
def db_factory(cfg_name):
    db = DBConnection(cfg_name)
    db.pool = yield db.get_pool()
    defer.returnValue(db)


## basehandler.py
def __init__(self, name=None, db=None):
    resource.Resource.__init__(self)
    self.name = name
    self.db = db
    self.pool = self.db.pool

@defer.inlineCallbacks
def runQuery(self, *args, **kwargs):
    res = yield self.pool.runQuery(*args, **kwargs)
    defer.returnValue(res)


## server.py
@defer.inlineCallbacks
def init_site(db):
    db = yield db_factory(db)
    root = RootURLHandler(db)
    reactor.listenTCP(SERVER_PORT, site)

def main(db):
    log.startLogging(LogFile('server.log', '.', maxRotatedFiles=5))
    init_site(db)
    reactor.run()

这个关键,也许不足为奇,就是让网站初始化成为延迟的数据库。