2011-11-16 126 views
2

我正在與Twisted的Web服務工作,負責調用我以前在命令行上使用過的幾個包。這些軟件包處理的例程正在自行開發,但現在已準備好集成到我們的Web服務中。扭曲的Web服務-sql連接丟失

總之,我有幾個不同的模塊,它們都在其原始命令行窗體中內部創建一個mysql連接屬性。藉此,例如:

class searcher: 
    def __init__(self,lat,lon,radius): 
    self.conn = getConnection()[1] 
    self.con=self.conn.cursor(); 

    self.mgo = getConnection(True) 

    self.lat = lat 
    self.lon = lon 
    self.radius = radius 
    self.profsinrange() 
    self.cache = memcache.Client(["173.220.194.84:11211"]) 

的GetConnection函數只是分別返回蒙戈或MySQL光標一個幫手。再次,這是全部原型:)

我遇到的問題是當作爲一個持續運行的服務器使用Twisted的WSGI資源實現時,在init超時創建的sql連接,並且後續請求似乎不會重新生成它。對於小型服務器應用程序的示例代碼:

from twisted.web import server 
from twisted.web.wsgi import WSGIResource 
from twisted.python.threadpool import ThreadPool 
from twisted.internet import reactor 
from twisted.application import service, strports 
import cgi 

import gnengine 
import nn 

wsgiThreadPool = ThreadPool() 
wsgiThreadPool.start() 

# ensuring that it will be stopped when the reactor shuts down 
reactor.addSystemEventTrigger('after', 'shutdown', wsgiThreadPool.stop) 


def application(environ, start_response): 
    start_response('200 OK', [('Content-type','text/plain')]) 
    params = cgi.parse_qs(environ['QUERY_STRING']) 
    try: 
     lat = float(params['lat'][0]) 
     lon = float(params['lon'][0]) 
     radius = int(params['radius'][0]) 
     query_terms = params['query'] 
     s = gnengine.searcher(lat,lon,radius) 
     query_terms = ' '.join(query_terms) 
     json = s.query(query_terms) 
     return [json] 
    except Exception, e: 
     return [str(e),str(params)] 

    return ['error'] 

wsgiAppAsResource = WSGIResource(reactor, wsgiThreadPool, application) 

# Hooks for twistd 
application = service.Application('Twisted.web.wsgi Hello World Example') 
server = strports.service('tcp:8080', server.Site(wsgiAppAsResource)) 
server.setServiceParent(application) 

最初的幾個要求做工精細,但mysqls wait_timeout到期後,恐懼錯誤2006年「MySQL已經走了」錯誤的表面。我的理解是,對WSGI Twisted資源的每個請求都將運行應用程序函數,從而重新生成搜索器對象並重新租用連接。如果情況並非如此,我如何能夠像這樣處理請求?在這種意義上,這種Twisted部署不是事務性的嗎?謝謝!

編輯:每請求時,這裏是原型輔助函數調用的連接:

def getConnection(mong = False): 
    if mong == False: 
    connection = mysql.connect(host = db_host, 
        user = db_user, 
        passwd = db_pass, 
        db = db, 
        cursorclass=mysql.cursors.DictCursor) 
    cur = connection.cursor(); 
    return (cur,connection) 
    else: 
    return pymongo.Connection('173.220.194.84',27017).gonation_test 
+0

你的MySQL連接創建邏輯隱藏在省略'getConnection'功能。如果包含該函數的定義以及發生的確切異常(並且最好是回溯),則問題可能會更清晰且更易於回答。 –

回答

2

我開發了一塊有扭曲的軟件,我必須利用恆定的MySQL數據庫連接。我遇到了這個問題,並深入挖掘扭曲的文檔,併發布了幾個問題,我無法找到合適的解決方案。當您實例化adbapi.connectionPool類時,您可以傳遞一個布爾參數;但它似乎從來沒有工作,我不斷收到錯誤。但是,我猜測重新連接布爾表示是SQL斷開連接時發生的連接對象的銷燬。

adbapi.ConnectionPool("MySQLdb", cp_reconnect=True, host="", user="", passwd="", db="") 

我還沒有測試過這個,但我會重新發布一些結果,當我做或如果任何其他人請分享。

當我在開發腳本時,我正在使用扭曲的8.2.0(我沒有碰到扭曲的一段時間),然後框架沒有這樣的顯式保持活着的方法,所以我開發了一個使用事件驅動的ping/keepalive擴展與直接MySQLdb模塊ping()方法(請參閱代碼註釋)一起構建的範型扭曲。 正如我輸入這個迴應;然而,我沒有看看目前扭曲的文檔,我仍然無法找到明確的保活方法或參數。我的猜測是因爲扭曲本身沒有數據庫連接庫/類。它使用python可用的方法,並提供與這些模塊接口的間接層;有一些直接調用正在使用的數據庫庫的風險。這是通過使用adbapi.runWithConnection方法完成的。

這裏是我在扭曲的8.2.0和python 2.6下編寫的模塊;你可以設置ping之間的時間間隔。腳本所做的是每20分鐘對數據庫進行一次ping,如果失敗,它會每60秒嘗試重新連接一次。我必須警告腳本不會處理突然/斷開的連接;你可以通過addErrback處理,只要你通過扭曲運行一個查詢,至少我是如何做到的。我注意到,每當數據庫連接斷開時,只有在執行查詢時纔會發現它是否存在,並且事件引發了錯誤,然後在那個時候處理它。基本上,如果我不運行10分鐘查詢,並且我的數據庫斷開連接,我的應用程序將不會實時響應。應用程序會意識到連接在運行後面的查詢時已經被刪除;所以數據庫可能已經斷開我們1分鐘後,第一個查詢,5,9等.... 我想這種回到原來的想法,我說,扭曲利用python自己的庫或第三方庫數據庫連接,並因此,有些事情處理有點不同。



from twisted.enterprise import adbapi 
from twisted.internet import reactor, defer, task 

class sqlClass: 
     def __init__(self, db_pointer): 
       self.dbpool=db_pointer 
       self.dbping = task.LoopingCall(self.dbping) 
       self.dbping.start(1200) #20 minutes = 1200 seconds; i found out that if MySQL socket is idled for 20 minutes or longer, MySQL itself disconnects the session for security reasons; i do believe you can change that in the configuration of the database server itself but it may not be recommended. 
       self.reconnect=False 
       print "database ping initiated" 

     def dbping(self): 
       def ping(conn): 
         conn.ping() #what happens here is that twisted allows us to access methods from the MySQLdb module that python posesses; i chose to use the native command instead of sending null commands to the database. 
       pingdb=self.dbpool.runWithConnection(ping) 
       pingdb.addCallback(self.dbactive) 
       pingdb.addErrback(self.dbout) 
       print "pinging database" 

     def dbactive(self, data): 
       if data==None and self.reconnect==True: 
         self.dbping.stop() 
         self.reconnect=False 
         self.dbping.start(1200) #20 minutes = 1200 seconds 
         print "Reconnected to database!" 
       elif data==None: 
         print "database is active" 

     def dbout(self, deferr): 
       #print deferr 
       if self.reconnect==False: 
         self.dbreconnect() 
       elif self.reconnect==True: 
         print "Unable to reconnect to database" 
       print "unable to ping MySQL database!" 

     def dbreconnect(self, *data): 
       self.dbping.stop() 
       self.reconnect=True 
       #self.dbping = task.LoopingCall(self.dbping) 
       self.dbping.start(60) #60 
if __name__ == "__main__": 
     db = sqlClass(adbapi.ConnectionPool("MySQLdb", cp_reconnect=True, host="", user="", passwd="", db="")) 
     reactor.callLater(2, db.dbping) 
     reactor.run()

讓我知道它是如何工作的你:)

+0

謝謝!今天我要試着實現這個。一個問題,因爲我通常不使用dbapi ...該類的什麼屬性是用於查詢的光標? – DeaconDesperado

+0

初始化adbapi類時,twisted不返回遊標,它返回用於運行查詢和執行數據庫操作的對象。扭曲,當你執行屬於你的adbapi實例的runQuery對象時,返回一個對象,你向它提供你的回調/ errback鏈接受結果。該框架以列表形式傳遞結果,您可以在其中迭代;這保持了基於事件的範式扭曲的意思。您可以訪問實際的光標,就像我從MySQLdb訪問ping()對象一樣,但這會破壞使用扭曲的目的。 – mayotic

+0

我會推薦查看這個http://twistedmatrix.com/documents/11.0.0/api/twisted.enterprise.adbapi.ConnectionPool.html,這裏是一個來自文檔的教程,它會給你一個關於什麼的相當好的概念我的意思是http://twistedmatrix.com/documents/current/core/howto/rdbms.html – mayotic