Уведомления

Группа в Telegram: @pythonsu

#1 Ноя. 1, 2012 08:23:16

lat
Зарегистрирован: 2012-07-04
Сообщения: 4
Репутация: +  0  -
Профиль   Отправить e-mail  

twisted. Остановка reactor'a

Добрый день. Принялся изучать twisted, но к сожалению слаб в техническом английском, поэтому много понять не могу . Вопрос в следующем, почему при попытке остановить реактор, выдает исключение (смотреть после кода) и как выстроить архитектуру приложения что бы такого не происходило.Цель с помощью callLater выставить timeout после которого Reactor будет остановлен, если данные с сервера не были получены за этот промежуток.

import optparse, sys
from twisted.internet.protocol import Protocol, ClientFactory
def parse_args():
    usage = """usage: %prog [options] [hostname]:port ...
This is the Get Poetry Now! client, Twisted version 3.1
Run it like this:
  python get-poetry-1.py port1 port2 port3 ...
If you are in the base directory of the twisted-intro package,
you could run it like this:
  python twisted-client-3/get-poetry-1.py 10001 10002 10003
to grab poetry from servers on ports 10001, 10002, and 10003.
Of course, there need to be servers listening on those ports
for that to work.
"""
    parser = optparse.OptionParser(usage)
    _, addresses = parser.parse_args()
    if not addresses:
        print parser.format_help()
        parser.exit()
    def parse_address(addr):
        if ':' not in addr:
            host = '127.0.0.1'
            port = addr
        else:
            host, port = addr.split(':', 1)
        if not port.isdigit():
            parser.error('Ports must be integers.')
        return host, int(port)
    return map(parse_address, addresses)
class PoetryProtocol(Protocol):
    poem = ''
    def dataReceived(self, data):
        self.poem += data
    def connectionLost(self, reason):
        self.poemReceived(self.poem)
    def poemReceived(self, poem):
        self.factory.poem_finished(poem)
class PoetryClientFactory(ClientFactory):
    protocol = PoetryProtocol
    def __init__(self, callback, errback):
        self.callback = callback
        self.errback = errback
    def poem_finished(self, poem):
        self.callback(poem)
    def clientConnectionFailed(self, connector, reason):
        print 'Connection Failed'
        self.errback(reason)
def get_poetry(host, port, callback, errback):
    """
    Download a poem from the given host and port and invoke
      callback(poem)
    when the poem is complete. If there is a failure, invoke:
      errback(err)
    instead, where err is a twisted.python.failure.Failure instance.
    """
    from twisted.internet import reactor
    factory = PoetryClientFactory(callback, errback)
    reactor.connectTCP(host, port, factory)
def poetry_main():
    addresses = parse_args()
    from twisted.internet import reactor
    poems = []
    errors = []
    def got_poem(poem):
        poems.append(poem)
        poem_done()
    def poem_failed(err):
        print >>sys.stderr, 'Poem failed:', err
        errors.append(err)
        poem_done()
    def poem_done():
        if len(poems) + len(errors) == len(addresses):
            reactor.stop()
    for address in addresses:
        host, port = address
        get_poetry(host, port, got_poem, poem_failed)
    reactor.run()
    for poem in poems:
        print poem
def go():
    from twisted.internet import reactor
    reactor.callLater(0.000001,poetry_main)
    reactor.callLater(2,stop)
def stop():
    from twisted.internet import reactor
    print 'Exceeded the threshold of response'
    reactor.stop()
if __name__ == '__main__':
    from twisted.internet import reactor
    reactor.callWhenRunning(go)
    reactor.run()
Unhandled Error
Traceback (most recent call last):
  File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 144, in <module>
    reactor.run()
  File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1169, in run
    self.mainLoop()
  File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1178, in mainLoop
    self.runUntilCurrent()
--- <exception caught here> ---
  File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 800, in runUntilCurrent
    call.func(*call.args, **call.kw)
  File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 124, in poetry_main
    reactor.run()
  File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1168, in run
    self.startRunning(installSignalHandlers=installSignalHandlers)
  File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 1148, in startRunning
    ReactorBase.startRunning(self)
  File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 678, in startRunning
    raise error.ReactorAlreadyRunning()
twisted.internet.error.ReactorAlreadyRunning: 
Exceeded the threshold of response
Unhandled Error
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\twisted\internet\defer.py", line 290, in addCallbacks
    self._runCallbacks()
  File "C:\Python27\lib\site-packages\twisted\internet\defer.py", line 551, in _runCallbacks
    current.result = callback(current.result, *args, **kw)
  File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 426, in _continueFiring
    callable(*args, **kwargs)
  File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 621, in disconnectAll
    failure.Failure(main.CONNECTION_LOST))
--- <exception caught here> ---
  File "C:\Python27\lib\site-packages\twisted\python\log.py", line 84, in callWithLogger
    return callWithContext({"system": lp}, func, *args, **kw)
  File "C:\Python27\lib\site-packages\twisted\python\log.py", line 69, in callWithContext
    return context.call({ILogContext: newCtx}, func, *args, **kw)
  File "C:\Python27\lib\site-packages\twisted\python\context.py", line 118, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "C:\Python27\lib\site-packages\twisted\python\context.py", line 81, in callWithContext
    return func(*args,**kw)
  File "C:\Python27\lib\site-packages\twisted\internet\tcp.py", line 473, in connectionLost
    self._commonConnection.connectionLost(self, reason)
  File "C:\Python27\lib\site-packages\twisted\internet\tcp.py", line 287, in connectionLost
    protocol.connectionLost(reason)
  File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 60, in connectionLost
    self.poemReceived(self.poem)
  File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 63, in poemReceived
    self.factory.poem_finished(poem)
  File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 75, in poem_finished
    self.callback(poem)
  File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 108, in got_poem
    poem_done()
  File "D:/Python/book/twistedbook/examples-twisted/twisted-client-3/get-poetry-1.py", line 117, in poem_done
    reactor.stop()
  File "C:\Python27\lib\site-packages\twisted\internet\base.py", line 577, in stop
    "Can't stop reactor that isn't running.")
twisted.internet.error.ReactorNotRunning: Can't stop reactor that isn't running.

Отредактировано slav0nic (Ноя. 1, 2012 10:28:45)

Офлайн

#2 Ноя. 1, 2012 10:27:14

lat
Зарегистрирован: 2012-07-04
Сообщения: 4
Репутация: +  0  -
Профиль   Отправить e-mail  

twisted. Остановка reactor'a

Уточню. То что ошибка указывает на невозможность остановки реактора, потому как он запущен и выполняет процессы я понимаю. Как мне избежать данного момента и остановить реактор не дожидаясь полного скачивания данных? Может вместо остановки реактора мне использовать raise connectionLost ? ну тогда скорей всего произойдет ошибка из-за вызова одного и того же callback'a одновременно, скорей всего. Возможно я не совсем правильно излагаю свои догадки, поэтому готов ответить на уточняющие вопросы и жду вашей помощи .

Офлайн

#3 Ноя. 1, 2012 10:54:27

bw
От:
Зарегистрирован: 2007-09-26
Сообщения: 938
Репутация: +  20  -
Профиль   Адрес электронной почты  

twisted. Остановка reactor'a

Ты не прав. Проблема в том что ты его дважды останавливаешь, собственно в сообщении об ошибке это и написано: Низя остановить _НЕ_ запущенный реактор.
Следующая проблема – это два run'а, избавься от них, или они избавятся от тебя :-).
Если у тебя несколько точек запуска реактора и несколько точек останова, то что-то не так, т.е. такое возможно, но так луше не делать. Постарайся построить архитектуру так, что бы `reactor.run()` и `reactor.stop()` присутствовали в коде в единственном экземпляре. Так же, если по какой-то причине `reactor.stop()` вызывается дважды, значит что-то не так, хотя повторный вызов stop'а можно избежать проверкой свойства `reactor.running`, но по мне, это признак плохого дизайна, который безусловно аукнется по тебе.

..bw



Офлайн

#4 Ноя. 1, 2012 11:54:06

lat
Зарегистрирован: 2012-07-04
Сообщения: 4
Репутация: +  0  -
Профиль   Отправить e-mail  

twisted. Остановка reactor'a

Да я понимаю, что архитектура страдает, нет опыта разработки приложений. Второй run закаментен, тут просто не отображено, извиняюсь. Избавиться от второго stop'a не знаю как, по идеи после таймаута callLetar он должен остановить связь с сервером, поэтому предположил что лучше будет использовать raise функции connectionLost, например. Но тогда может произойти вызов двух callback одновременно, что опять же вызовет ошибку.

def connectionLost(self, reason):
        self.poemReceived(self.poem)
и не могу понять что передать этой функции первым аргументом, экземпляр класса где она описана?

Отредактировано lat (Ноя. 1, 2012 11:54:51)

Офлайн

#5 Ноя. 1, 2012 20:04:53

bw
От:
Зарегистрирован: 2007-09-26
Сообщения: 938
Репутация: +  20  -
Профиль   Адрес электронной почты  

twisted. Остановка reactor'a

Понятно. Более правильным решением будет принудительно завершить все “работы”, что приведёт к вызову каждой из них метода `poem_done` и в конечном итоге остановке реактора. А явный вызов `stop` я бы убрал. Хотя можно по прежнему использовать `stop` явно, а в `poem_done` добавить проверку того работает ли реактор или нет, как писал выше. Есть и третий вариант, вместо `reactor.stop()` в `poem_done` выставлять где-нибудь флаг, что мы тут закончили, а в отдельном “потоке” (например используя `LoopingCall`) мониторить это дело и вызывать `stop`. Для данного примера я бы использовал первый вариант.
Для первого случая. Пишу по памяти :-). Уже больше двух лет не пользовал Twisted, так что могу ошибаться. Тут должен помочь вызов `transport.loseConnection` в каждом из протоколов, а как до них докопаться не скажу, а то будет не интересно :-). Могу только сказать, что не хорошо будет включать логику по прирыванию соединения в сами протоколы, она должна действовать извне.

..bw



Офлайн

#6 Ноя. 2, 2012 11:37:07

lat
Зарегистрирован: 2012-07-04
Сообщения: 4
Репутация: +  0  -
Профиль   Отправить e-mail  

twisted. Остановка reactor'a

Решил проблему обрубив все соединения. Хоть и не совсем эстетично, но в данном случаи как раз это и надо было. Воспользовался, кому интересно, reactor.disconnectAll().
bw Спасибо за советы и удачи!

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version