asyncio event_loop в приложении Flask
Каков наилучший способ запуска цикла событий asyncio в приложении Flask?
Мой main.py выглядит так:
if __name__ == '__main__':
try:
app.run(host='0.0.0.0', port=8000, debug=True)
except:
logging.critical('server: CRASHED: Got exception on main handler')
logging.critical(traceback.format_exc())
raise
Чтобы добавить опцию асинхронных задач, мне нужно было создать event_loop
перед запуском app
, но даже при остановке запуска приложения фоновый поток все еще зависает (это можно наблюдать в отладчике)
if __name__ == '__main__':
try:
app.event_loop = asyncio.get_event_loop()
app.run(host='0.0.0.0', port=8000, debug=True)
except:
logging.critical('server: CRASHED: Got exception on main handler')
logging.critical(traceback.format_exc())
raise
finally:
app.event_loop.stop()
app.event_loop.run_until_complete(app.event_loop.shutdown_asyncgens())
app.event_loop.close()
И используя следующее для создания асинхронных задач:
def future_callback(fut):
if fut.exception():
logging.error(fut.exception())
def fire_and_forget(func, *args, **kwargs):
if callable(func):
future = app.event_loop.run_in_executor(None, func, *args, **kwargs)
future.add_done_callback(future_callback)
else:
raise TypeError('Task must be a callable')
Единственное решение, которое я смог найти, это добавить exit()
в конце finally
блок, но я не думаю, что это правильное решение.
1 ответ
Первоначально я использовал подход 1 event_loop для всего приложения фляги, но мне это не понравилось по нескольким причинам.
Вместо этого я создал вспомогательный файл, custom_flask_async.py
с 2 функциями,
import asyncio
def get_set_event_loop():
try:
return asyncio.get_event_loop()
except RuntimeError as e:
if e.args[0].startswith('There is no current event loop'):
asyncio.set_event_loop(asyncio.new_event_loop())
return asyncio.get_event_loop()
raise e
def run_until_complete(tasks):
return get_set_event_loop().run_until_complete(asyncio.gather(*tasks))
Я только проверяю и get_set
event_loop, если он будет использоваться и ожидался в конкретном запросе. И потому, что основной способ, которым я использую это с run_until_complete
с собранием, я тоже в помощнике.
Я также не уверен, является ли это лучшим подходом, или какие недостатки есть у этого метода, но он определенно работает для моей цели.
В конце концов, лучшее решение, которое я нашел, было разместить мое приложение Flask в uwsgi
который позволяет использовать mules
а также spooler
для асинхронных задач