2011-08-09 97 views
2

我正在實現一個庫模塊以用作couchdb更改通知的客戶端。我想讓庫處於「連續」模式,也就是說,連接必須永遠保持打開狀態,或者至少在連接已關閉的情況下它必須重新連接,以便couchdb有一個通道來通知發生在數據庫。然後,我將處理這些通知以生成特定事件(這尚未實施)。以扭曲形式實現重新連接http客戶端

我選擇的方法是使用ReconnectingClientFactory(它根據詳細的算法自動重新連接)作爲我的協議工廠的基礎。無論何時建立連接,都會調用buildProtocol方法。在這個方法中,我創建了協議實例,然後觸發一個callLater(立即)來表明連接已準備就緒。在cdConnected函數中,我發送請求並添加一個回調來處理接收到的數據(cbReceived)。

代碼中並重新連接如預期,但我有兩個不同的問題:

  • 請求失敗(沒有數據通過TCP連接發送的),但我不知道爲什麼。
  • 即使連接完全關閉,也會生成錯誤。

也許有人知道我做錯了什麼?

謝謝!

(編輯:在「連接被完全關閉。」正在打印由自己的錯誤,所以可以忽略不計)

下面是代碼:

from twisted.internet    import defer 
from twisted.internet.protocol  import ReconnectingClientFactory 
from twisted.web._newclient  import HTTP11ClientProtocol 
from twisted.web._newclient  import Request 
from twisted.web.client   import _parse 

class MyReconnectingClientFactory(ReconnectingClientFactory): 

    def __init__(self, reactor, cbConnected): 
     self.reactor  = reactor 
     self.cbConnected = cbConnected 

    def startedConnecting(self, connector): 
     print 'Started to connect ...' 

    def buildProtocol(self, addr): 
     print 'Resetting reconnection delay' 
     self.resetDelay() 
     proto = HTTP11ClientProtocol() 
     self.reactor.callLater(0, self.cbConnected, proto) 
     return proto 

    def clientConnectionLost(self, connector, reason): 
     print 'Lost connection. Reason:', reason 
     ReconnectingClientFactory.clientConnectionLost(self, connector, reason) 

    def clientConnectionFailed(self, connector, reason): 
     print 'Connection failed. Reason:', reason 
     ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) 

def cbReceived(response): 
    print response 

def printError(failure): 
    print "printError > %s" % (str(failure)) 

def cbConnected(proto): 
    print "Sending request ..." 
    req = Request(method, path, headers, bodyProducer) 
    d = proto.request(req) 
    d.addCallback(cbReceived).addErrback(printError) 
    return d 

from twisted.internet import reactor 

uri='http://localhost:5984/cn/_changes?feed=continuous' 
method='GET' 
headers=None 
bodyProducer=None 

scheme, host, port, path = _parse(uri) 
factory = MyReconnectingClientFactory(reactor, cbConnected) 
reactor.connectTCP(host, port, factory) 
reactor.run() 

這裏是輸出:

Started to connect ... 
Resetting reconnection delay 
Sending request ... 
printError > [Failure instance: Traceback (failure with no frames): <class 'twisted.web._newclient.RequestGenerationFailed'>: [<twisted.python.failure.Failure <type 'exceptions.AttributeError'>>] 
] 
Lost connection. Reason: [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionDone'>: Connection was closed cleanly. 
] 
Started to connect ... 
Resetting reconnection delay 
Sending request ... 
printError > [Failure instance: Traceback (failure with no frames): <class 'twisted.web._newclient.RequestGenerationFailed'>: [<twisted.python.failure.Failure <type 'exceptions.AttributeError'>>] 
] 
Lost connection. Reason: [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionDone'>: Connection was closed cleanly. 
] 
+0

'_newclient'是Twisted中的私有模塊。請注意,如果您從中導入名稱,則任何新版本的Twisted中的代碼都可能會中斷而不會發出警告。如果您需要支持此私有模塊的功能,請在http://twistedmatrix.com/上提交錯誤。 – Glyph

回答

5

你應該看看產生的故障。 _newclient有拋出複合失敗的習慣;這些包含更多的失敗。

我適應您的代碼一點:

def printError(failure): 
print "printError > %r" % failure 

from twisted.web import _newclient 
if failure.check(_newclient.RequestGenerationFailed): 
    print "printError: RequestGenerationFailed" 
    for f in failure.value.reasons: 
     print "printError > %r" % f 
     print f.getTraceback() 

你正在做一個STR()失敗,這就是不要讓有關的異常信息的好方法。讓repr照顧它,那是它的工作。

使用%r,我看到它實際上給了我一個RequestGenerationFailed。這是一個更有趣的失敗。失敗值有其原因。

隨着我的修改,劇本給了這一點:

Started to connect ... 
Resetting reconnection delay 
Sending request ... 
printError > <twisted.python.failure.Failure <class 'twisted.web._newclient.RequestGenerationFailed'>> 
printError: RequestGenerationFailed 
printError > <twisted.python.failure.Failure <type 'exceptions.AttributeError'>> 
Traceback (most recent call last): 
    File "/usr/lib64/python2.7/site-packages/twisted/internet/base.py", line 1174, in mainLoop 
    self.runUntilCurrent() 
    File "/usr/lib64/python2.7/site-packages/twisted/internet/base.py", line 796, in runUntilCurrent 
    call.func(*call.args, **call.kw) 
    File "so.py", line 50, in cbConnected 
    d = proto.request(req) 
    File "/usr/lib64/python2.7/site-packages/twisted/web/_newclient.py", line 1266, in request 
    _requestDeferred = maybeDeferred(request.writeTo, self.transport) 
--- <exception caught here> --- 
    File "/usr/lib64/python2.7/site-packages/twisted/internet/defer.py", line 125, in maybeDeferred 
    result = f(*args, **kw) 
    File "/usr/lib64/python2.7/site-packages/twisted/web/_newclient.py", line 703, in writeTo 
    self._writeHeaders(transport, None) 
    File "/usr/lib64/python2.7/site-packages/twisted/web/_newclient.py", line 535, in _writeHeaders 
    hosts = self.headers.getRawHeaders('host',()) 
exceptions.AttributeError: 'NoneType' object has no attribute 'getRawHeaders' 

Lost connection. Reason: [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionDone'>: Connection was closed cleanly. 
] 

這應該給你上哪裏找你的實際問題的一個很好的線索。

順便說一下,看看佩斯利,一個扭曲的客戶端的CouchDB:paisley

有幾個分支,特別是my changes branch有一些您可能感興趣的變更通知的東西,我做了一個桌面小程序向我展示了添加到我的基於CouchDB的待辦事項系統的任務。

這聽起來像你的變化是:a)已經在那裏或b)應該在那裏;和c)你應該考慮與佩斯利合作併爲之作出貢獻。

祝你好運!

+0

謝謝Thomas,這非常有幫助。我將使用你的輸入來調試我的程序。關於佩斯利:我看到你有幾個開放的分支。您是否打算將這些合併到主分支(特別是更改分支)。一旦我熟悉它,我一定會考慮做出貢獻。 – dangonfast

+0

我發現問題:對於請求,標題不能爲None。儘管我們已經在連接階段提供了這些信息,但至少必須指定主機。隨着這些變化,我得到一個_newclient.Response,但我不知道如何使用它來獲取couchdb數據。我希望能得到json數據... – dangonfast

+0

是的,他們應該合併。我正在完成一個項目,使用它們來確保更改是正確的。至於響應 - 我認爲你必須註冊deliverBody才能獲得正文。佩斯利被移植到_newclient,所以看看。 –