Реализация переподключения HTTP-клиента в витой - PullRequest
2 голосов
/ 09 августа 2011

Я реализую библиотечный модуль, который будет служить клиентом для уведомлений об изменениях в couchdb.Я хочу, чтобы библиотека находилась в «непрерывном» режиме, то есть соединение должно оставаться открытым всегда, или, по крайней мере, оно должно быть повторно подключено, если соединение было закрыто, чтобы у couchdb был канал для уведомления о любых новых изменениях, происходящих вбаза данных.Затем я обработаю эти уведомления для генерации определенных событий (это еще не реализовано).

Подход, который я выбрал, состоит в том, чтобы использовать ReconnectingClientFactory (которая выполняет автоматическое повторное соединение согласно разработанному алгоритму) в качестве основыдля моего протокола Factory.Всякий раз, когда соединение установлено, вызывается метод 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 голосов
/ 16 августа 2011

Вы должны взглянуть на сгенерированные сбои. _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 () при ошибке, и это не очень хороший способ получить информацию об исключении. Пусть репр позаботится об этом, это его работа.

Используя% 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.
]

Это должно дать вам хорошее представление о том, где искать вашу актуальную проблему.

Кстати, взгляните на Пейсли, клиент Twisted для CouchDB: Пейсли

Есть несколько веток, и, в частности, В моей ветке изменений есть кое-что, что может вас заинтересовать. Я создал настольный апплет, который показывал мне задачи, добавленные в мою todo-систему на основе CouchDB.

Похоже, ваши изменения: а) уже есть или б) должны войти туда; и c) вам следует подумать о работе с Пейсли и о ее содействии.

Удачи!

...