Как использовать python tornado @ gen.coroutine с длительными функциями - PullRequest
0 голосов
/ 30 мая 2020

У меня есть веб-приложение, которое также выполняет очень интенсивную обработку данных. Некоторые функции работают очень медленно (подумайте пару минут).

До сих пор у меня была архитектура, порождающая новые потоки / процесс для каждого соединения, поэтому эти медленные функции не блокируют других пользователей. Но это потребляет слишком много памяти и противоречит архитектуре торнадо.

Поэтому мне интересно, есть ли решение этой проблемы. Мой код выглядит так:

# code that is using to much memory because of the new threads being spawned 
def handler():
   thread = Thread(target = really_slow_function)
   thread.start()
   thread.join()
   return "done"

def really_slow_function():
   # this is an example of an intensive function
   # which should be treated as a blackbox
   sleep(100)
   return "done"

После рефакторинга у меня есть следующий код:

#code that doesn't scale because all the requests are block on that one slow request.
@gen.coroutine
def handler():
   yield really_slow_function()
   raise gen.Return("done")

def really_slow_function():
   # this is an example of an intensive function
   # which should be treated as a blackbox
   sleep(100)
   return "done"

Проблема с этим рефакторингом заключается в том, что сервер торнадо блокирует really_slow_function и тем временем не может обслуживать другие запросы.

Итак, вопрос: есть ли способ рефакторинга обработчика БЕЗ касания really_slow_function и БЕЗ создания новых потоков / процессов?

1 Ответ

1 голос
/ 30 мая 2020

Используйте ThreadPoolExecutor (из пакета concurrent.futures) для запуска длительной функции в отдельных потоках без запуска каждый раз нового потока.

async def handler():
    await IOLoop.current().run_in_executor(None, really_slow_function)
    return "done"

Если вы хотите точно контролировать, сколько потоки могут запускать эту функцию, вы можете создать своего собственного исполнителя и передать его вместо None.

...