Изящно остановить Tornado ioLoop

У меня есть эта асинхронная рабочая функциональность, использующая ioloop торнадо. Я пытаюсь изящно завершить цикл нажатием Ctrl+C, но получаю следующую ошибку

tornado.ioloop.TimeoutError: время ожидания операции истекло через None секунд

Я знаю, что могу поймать его, но я хочу закончить процесс изящно, как я могу этого добиться?

#!/usr/bin/env python
import time
import signal
import random

from tornado import gen, ioloop, queues

concurrency = 10

def sig_exit(signum, frame):
    ioloop.IOLoop.current().add_callback_from_signal(shutdown)

def shutdown():
    print('Will shutdown in few seconds ...')
    io_loop = ioloop.IOLoop.current()

    deadline = time.time() + 3

    def stop_loop():
        now = time.time()
        if now < deadline and (io_loop._callbacks or io_loop._timeouts):
            io_loop.add_timeout(now + 1, stop_loop)
        else:
            io_loop.stop()
            print('Shutdown')

    stop_loop()

@gen.coroutine
def main():
    q = queues.Queue()
    q.put(1)

    @gen.coroutine
    def do_stuff():
        print("doing stuff")
        yield gen.Task(ioloop.IOLoop.instance().add_timeout, time.time() + random.randint(1, 5))
        print("done doing stuff")

    @gen.coroutine
    def worker():
        while True:
            yield do_stuff()

    for _ in range(concurrency):
        worker()

    yield q.join()


if __name__ == '__main__':
    signal.signal(signal.SIGTERM, sig_exit)
    signal.signal(signal.SIGINT, sig_exit)

    io_loop = ioloop.IOLoop.instance()
    io_loop.run_sync(main)

person shahaf    schedule 17.04.2018    source источник
comment
Вы пытались выполнить поиск в Google по этой теме? Многие ссылки всплывают с обсуждениями, связанными с этим. Но вот ответ от Бена Дарнелла, опубликованный в списке рассылки Tornado. . И этот вопрос здесь, на SO, содержит код, соответствующий вашим интересам, и хорошо объясненный ответ.   -  person xyres    schedule 17.04.2018
comment
@xyres, спасибо, я искал в Google, большая часть темы, посвященной прекращению действия http server, встроенного в ioloop, не нашла кого-то, кто сделал это с run_async, как в моем случае...   -  person shahaf    schedule 18.04.2018


Ответы (1)


Если вы используете run_sync, вы больше не можете вызывать IOLoop.stop — теперь за это отвечает run_sync. Поэтому, если вы хотите сделать это завершение «изящным» (вместо того, чтобы просто вызывать KeyboardInterrupt в точке, где вы сейчас вызываете stop(), и выходить с трассировкой стека), вам нужно изменить сопрограмму, переданную на run_sync, чтобы она завершилась.

Одним из возможных решений является tornado.locks.Event:

# Create a global Event
shutdown_event = tornado.locks.Event()

def shutdown():
    # Same as in the question, but instead of `io_loop.stop()`:
    shutdown_event.set()

@gen.coroutine
def main():
    # Use a WaitIterator to exit when either the queue 
    # is done or shutdown is triggered. 
    wait_iter = gen.WaitIterator(q.join(), shutdown_event.wait())
    # In this case we just want to wait for the first one; we don't
    # need to actually iterate over the WaitIterator. 
    yield wait_iter.next()
person Ben Darnell    schedule 19.04.2018