我有一个使用Flask和Graphene包构建的GraphQL API,运行在 Python 3.5.4 上。其中一个GraphQL突变需要一些时间来执行(2到5分钟),我不希望最终用户等待其执行完成。我希望在后台执行变异方法,并立即向用户返回一条消息。
我已经查看了 asyncio 包但由于某种原因,执行仍然出现在最前端,我的脚本正在等待。你知道我做错了什么吗?脚本很长,所以我在下面总结了与asyncio相关的关键元素。
文件mutation_migration_plan.py
from migration_script import Migration, main
import asyncio
[...]
class executeMigrationPlan(graphene.Mutation):
"""Mutation to execute a migration plan."""
[...]
@staticmethod
def mutate(root, info, input):
[...]
# Execute migration asynchronously
print('Execute migration asynchronously')
loop = asyncio.get_event_loop()
loop.run_until_complete(main(migration_plan))
print('Migration plan execution has started. You will receive an e-mail when it is terminated.')
ok = True
message = 'Migration plan execution has started. You will receive an e-mail when it is terminated.'
return executeMigrationPlan(ok=ok, message=message)
文件migration_script.py
class Migration():
"""Class to execute migration of Plan, Step, Object."""
@staticmethod
async def migrate(migration_plan, migration_step=None, migration_object=None):
[...]
async def main(migration_plan, migration_step=None, migration_object=None):
asyncio.ensure_future(Migration.migrate(migration_plan, migration_step, migration_object))
基本上我希望在方法print('Migration plan execution has started. You will receive an e-mail when it is terminated.')
中几乎立即在我的控制台窗口中看到loop.run_until_complete(main(migration_plan))
,但现在情况并非如此,并且打印仅出现在执行结束。
[UPDATE]
根据以下@Vincent的回答,我更新了第一个文件文件mutation_migration_plan.py 以使用 ThreadPoolExecutor ,并从两个文件中删除了与asyncio相关的所有内容。
文件mutation_migration_plan.py
from migration_script import Migration
from concurrent.futures import ThreadPoolExecutor
[...]
class executeMigrationPlan(graphene.Mutation):
"""Mutation to execute a migration plan."""
[...]
@staticmethod
def mutate(root, info, input):
[...]
# Execute migration asynchronously
print('Execute migration asynchronously')
executor = ThreadPoolExecutor(max_workers=1)
future = executor.submit(Migration.migrate, migration_plan)
# print(future.result())
print('Migration plan execution has started. You will receive an e-mail when it is terminated.')
ok = True
message = 'Migration plan execution has started. You will receive an e-mail when it is terminated.'
return executeMigrationPlan(ok=ok, message=message)
当我添加print(future.result())
行时,我的脚本运行正常,但它不在后台执行(因为我试图打印结果,所以有意义)。但是,当我注释掉打印时,我的方法Migration.migrate
似乎没有正确执行(我知道它,因为我没有在我的数据库中看到结果)。知道为什么吗?
[UPDATE BIS]
文件mutation_migration_plan.py
我已经能够使用ProcessPoolExecutor异步执行我的方法,并在两个文件上删除对asyncio的所有引用。请参阅以下代码:
文件mutation_migration_plan.py
from concurrent.futures import ProcessPoolExecutor
[...]
class executeMigrationPlan(graphene.Mutation):
"""Mutation to execute a migration plan."""
[...]
@staticmethod
def mutate(root, info, input):
[...]
# Execute migration asynchronously
print('Execute migration asynchronously')
executor = ProcessPoolExecutor()
executor.submit(Migration.migrate, migration_plan.id)
print('Migration plan execution has started. You will receive an e-mail when it is terminated.')
ok = True
message = 'Migration plan execution has started. You will receive an e-mail when it is terminated.'
return executeMigrationPlan(ok=ok, message=message)
它正在工作但是虽然进程在后端执行,但我的Falsk应用程序需要很长时间才能发送http响应,而且响应有时是空的。
答案 0 :(得分:3)
这是一种常见的误解,但您无法将asyncio插入现有应用程序并期望它能够正常运行。在asyncio中,每个阻塞调用都必须在协同程序的上下文中使用await
语法。这是它实现单线程并发的唯一方法。这意味着您必须使用aiohttp代替烧瓶,以及aiohttp-graphql等库。
这需要对您的应用程序进行重大改写。如果您不想通过它,还有其他解决方案可以很好地与烧瓶集成。您可以使用@dirn指定的celery或concurrent.futures提供的执行程序之一。