2014-10-02 80 views
2

我們有一個使用txpostgres訪問postgres數據庫的RESTful(-ish)扭曲應用程序。目前,每次客戶端爲服務器調用db調用時,我們都會生成新的txpostgres.Connection實例。這是低效的,並導致我們的db很快變得不知所措。我一直在嘗試改編,以代替txpostgres.ConnectionPool,但遇到了麻煩。現在我有一些看起來像這樣:共享一個txpostgres連接池

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的一些最簡單的例子。有什麼建議麼?

+0

爲什麼你在DBTester類中有池?這是有原因的嗎? – brunsgaard 2014-10-03 13:42:25

回答

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() 

的關鍵,也許並不奇怪,在做網站時初始化數據庫的東西延期隊伍去通過。

+0

我真的覺得你應該重新考慮。這段代碼的一部分是可怕的,遺憾的是,特別是'txpostgres.AlreadyConnected'部分除外。但它是有效的。但它永遠不會通過質量保證我在哪裏工作.. – brunsgaard 2014-10-03 20:05:58

+0

這是前QA代碼 - 沒有人回答我的問題,我得到了一個MVP,所以我認爲我會做正確的事情,分享我學到的東西在我自己的。 – swizzard 2014-10-04 16:39:10

3

這聽起來像你的問題不是與txpostgres,但更多的扭曲和異步的思維方式。

exceptions.AttributeError:「NoneType」對象有沒有屬性「runQuery」的意思是: 你試過數據庫之後扔SQL查詢製成的連接之前。那太蠢了!所以現在我想我會拋出一個異常,所以親愛的用戶知道這個瘋狂。

所以,如果你有這樣的事情

pool = ConnectionPool(None, min=1) 
d1 = pool.start() 
d2 = pool.runQuery('select tablename from pg_tables') 

這可能發生此代碼創建了兩個deferreds和thorws時間在反應器中。只有調度算法知道哪一個先執行,如果是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() 

希望這會有所幫助。