首页 文章

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

提问于
浏览
2

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

我选择的方法是使用ReconnectingClientFactory(根据精心设计的算法自动重新连接)作为我的协议工厂的基础 . 无论何时 Build 连接,都会调用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.
]

1 回答

  • 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.
    ]
    

    这应该会给你一个很好的线索,在哪里寻找你的实际问题 .

    顺便提一下,看看Paisley,CouchDB的Twisted客户端:paisley

    有几个分支,特别是my changes branch有一些您可能感兴趣的更改通知内容 . 我制作了一个桌面小程序,向我展示了添加到基于CouchDB的todo系统的任务 .

    听起来你的变化是a)已经在那里或b)应该去那里; c)你应该考虑与佩斯利合作并做出贡献 .

    祝好运!

相关问题