Couchdb 在twisted中实现重新连接http客户端

Couchdb 在twisted中实现重新连接http客户端,couchdb,twisted,Couchdb,Twisted,我正在实现一个库模块,作为couchdb更改通知的客户端。我希望库处于“连续”模式,也就是说,连接必须永远保持打开状态,或者如果连接已关闭,至少它必须重新连接,以便couchdb有一个通道来通知数据库中发生的任何新更改。然后,我将处理这些通知以生成某些事件(这尚未实现) 我选择的方法是使用ReconnectingClientFactory(根据详细的算法自动重新连接)作为协议工厂的基础。只要建立了连接,就会调用buildProtocol方法。在这个方法中,我创建了protocol实例,并在稍后(

我正在实现一个库模块,作为couchdb更改通知的客户端。我希望库处于“连续”模式,也就是说,连接必须永远保持打开状态,或者如果连接已关闭,至少它必须重新连接,以便couchdb有一个通道来通知数据库中发生的任何新更改。然后,我将处理这些通知以生成某些事件(这尚未实现)

我选择的方法是使用ReconnectingClientFactory(根据详细的算法自动重新连接)作为协议工厂的基础。只要建立了连接,就会调用buildProtocol方法。在这个方法中,我创建了protocol实例,并在稍后(立即)发出一个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.
]
已开始连接。。。
重置重联延迟
正在发送请求。。。
printError>[故障实例:回溯(没有帧的故障):[]
]
失去联系。原因:[失败实例:回溯(没有帧的失败)::连接已完全关闭。
]
开始连接。。。
重置重联延迟
正在发送请求。。。
printError>[故障实例:回溯(没有帧的故障):[]
]
失去联系。原因:[失败实例:回溯(没有帧的失败)::连接已完全关闭。
]

您应该查看生成的故障_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.
]
已开始连接。。。
重置重联延迟
正在发送请求。。。
打印错误>
printError:RequestGenerationFailed
打印错误>
回溯(最近一次呼叫最后一次):
文件“/usr/lib64/python2.7/site packages/twisted/internet/base.py”,第1174行,在mainLoop中
self.rununtlcurrent()
文件“/usr/lib64/python2.7/site packages/twisted/internet/base.py”,第796行,在rununtlcurrent中
call.func(*call.args,**call.kw)
cbConnected中第50行的文件“so.py”
d=协议请求(req)
请求中的文件“/usr/lib64/python2.7/site packages/twisted/web/_newclient.py”,第1266行
_requestDeferred=maybeDeferred(request.writeTo,self.transport)
---  ---
文件“/usr/lib64/python2.7/site packages/twisted/internet/defer.py”,第125行,格式为maybeDeferred
结果=f(*参数,**kw)
文件“/usr/lib64/python2.7/site packages/twisted/web/_newclient.py”,第703行,以书面形式
自写标题(传输,无)
文件“/usr/lib64/python2.7/site packages/twisted/web/_newclient.py”,第535行,以书面形式出现
hosts=self.headers.getRawHeaders('host',())
exceptions.AttributeError:“非类型”对象没有属性“getRawHeaders”
失去联系。原因:[失败实例:回溯(没有帧的失败)::连接已完全关闭。
]
这会给你一个很好的线索,告诉你在哪里可以找到你的实际问题

顺便说一句,看看Paisley,CouchDB的扭曲客户端:

这里有几个分支,特别是一些您可能感兴趣的变更通知内容。我制作了一个桌面小程序,显示添加到基于CouchDB的todo系统中的任务

听起来你的改变是a)已经在那里或者b)应该在那里;C)你应该考虑和Paisley一起工作和贡献。


祝你好运

谢谢托马斯,这很有帮助。我将使用您的输入调试我的程序。关于佩斯利:我看到你有几个开着的分行。您是否计划将这些合并到主分支(特别是变更分支)。当我熟悉它的时候,我一定会考虑投稿。我发现问题是:对于这个请求,页眉是不可能的。至少必须指定主机,即使我们已经在连接阶段提供了该信息。通过这些更改,我得到了一个_newclient.Response,但我不知道如何使用它从中获取couchdb数据。我希望得到json数据……是的,它们应该合并。我正在完成一个项目,使用它们来确保更改是正确的。至于回应,我认为你必须向送货人登记才能拿到尸体。Paisley被移植到了_newclient,所以请看一看。您真的不应该导入
\u newclient
,因为它是一个私有实现模块。但是,
RequestGenerationFailed
缺少公共名称是一个缺陷。请参阅。
\u newclient
是Twisted中的专用模块。请注意,如果从中导入名称,则在任何新版本的Twisted中,代码可能会在没有警告的情况下中断。如果您需要支持此专用模块的功能,请在提交错误。